💾

【Flutter】Driftのマイグレーション

2023/05/03に公開

はじめに

Driftのマイグレーションについての記事になります。

DBに加える変更によってマイグレーション時の処理がところどころ変わってくるので、最初に基本の流れを説明してから各変更に対応したマイグレーション時の処理について記載していきます。

今回使用した環境は以下になります。

Flutter 3.7.12
Drift 2.7.0

基本の流れ

カラムの追加を通して、基本的なマイグレーションの流れを見ていきます。

以下の流れで説明していきます。

  1. テーブル修正
  2. DBクラス修正
  3. コード生成

1. テーブル修正

今回は下記のidtitleを持つテーブルに新たにcontentというカラムを加えてみます。

class Todos extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get title => text().withLength(min: 6, max: 32)();
+ TextColumn get content => text().withDefault(const Constant('デフォルト値'))();
}

上記問題は以下のような方法で解決できます。

  • デフォルト値を設定する(上記例ではこちらを採用)
  • nullableのカラムにする

2. DBクラス修正

続いてDBクラスの修正を行います。

schemaVersionをインクリメントし、migrationをオーバーライドし処理を書いていきます。

マイグレーションの処理はonUpgradeに書いていきます。(onCreateはDB初回読み込み時の処理を書く)

if (from < 2)という条件を加えることで、DBのバージョンが2未満の場合にこのマイグレーション処理が行われるように制御を入れます。また、今回はカラムを追加するのでMigratoraddColumn()メソッドを使用します。

@DriftDatabase(tables: [Todos])
class MyDatabase extends _$MyDatabase {
  MyDatabase() : super(_openConnection());

  
- int get schemaVersion => 1;
+ int get schemaVersion => 2;

+ 
+ MigrationStrategy get migration {
+   return MigrationStrategy(
+     onCreate: (Migrator m) async {
+       await m.createAll();
+     },
+     onUpgrade: (Migrator m, int from, int to) async {
+       if (from < 2) {
+         await m.addColumn(todos, todos.content);
+       }
+     },
+   );
+ }
}

3. コード生成

最後に下記コマンドでコードを生成して完了です。

flutter pub run build_runner build

ビルドして実行後、drift_db_viewerを使用してでDBの中身を確認すると、contentカラムが追加されていることと、デフォルト値が設定されていることを確認できました。

マイグレーション前 マイグレーション後

その他のマイグレーション処理

ここからは、上記の基本の流れで使用したコードを元に、他の操作を行う場合のマイグレーション処理についていくつか記載していきます。

テーブル追加・削除

テーブルの追加・削除はMigratorcreateTable(), deleteTable()メソッドで行うことができます。

ここでは下記のカテゴリーテーブルを追加する例を載せます。

("Category")
class Categories extends Table {
  IntColumn get id => integer().autoIncrement()();

  TextColumn get description => text()();
}
- (tables: [Todos])
+ (tables: [Todos, Categories])
class MyDatabase extends _$MyDatabase {
  MyDatabase() : super(_openConnection());

  
- int get schemaVersion => 2;
+ int get schemaVersion => 3;

  
  MigrationStrategy get migration {
    return MigrationStrategy(
      onCreate: (Migrator m) async {
        await m.createAll();
      },
      onUpgrade: (Migrator m, int from, int to) async {
        if (from < 2) {
          await m.addColumn(todos, todos.content);
        }
+       if (from < 3) {
+         await m.createTable(categories);
+       }
      },
    );
  }
}

カラム削除・制約変更

カラム削除・制約変更はMigratoralterTable()メソッドで行うことができます。

titleの文字数制約の変更とcontentの削除を行う例を載せます。

class Todos extends Table {
  IntColumn get id => integer().autoIncrement()();
- TextColumn get title => text().withLength(min: 6, max: 32)();
+ TextColumn get title => text().withLength(min: 10, max: 20)();
- TextColumn get content => text().withDefault(const Constant('デフォルト値'))();
}

onUpgrade内で下記を呼べばOKです。(全体像は省略)

await m.alterTable(TableMigration(todos));

カラムのリネーム

カラムのリネームにはいくつか方法があるのでそれぞれ紹介していきます。

1. named()

実際のDB上のカラム名を変える必要がない場合は、named()を使用することでマイグレーション無しでリネームが可能のようです。
コード上では変更後のカラム名で扱うことができるようになります。

titlesubtitleにリネームする例を載せます。

- TextColumn get title => text().withLength(min: 10, max: 20)();
+ TextColumn get subtitle => text().named('title').withLength(min: 10, max: 20)();

2. MigratorrenameColumn

アプリがsqlite 3.25.0以降で動作することがわかっている場合は、MigratorrenameColumnを使用することができます。

まず、テーブルのtitlesubtitleにリネームする修正を加えます。

- TextColumn get title => text().withLength(min: 10, max: 20)();
+ TextColumn get subtitle => text().withLength(min: 10, max: 20)();

onUpgrade内でrenameColumnを呼びます。

  • 第1引数: テーブル
  • 第2引数: リネーム前のカラム名
  • 第3引数: リネーム後のカラム
m.renameColumn(todos, 'title', todos.subtitle);

3. columnTransformer

2と同様、まずテーブルのtitlesubtitleにリネームする修正を加えます。

- TextColumn get title => text().withLength(min: 10, max: 20)();
+ TextColumn get subtitle => text().withLength(min: 10, max: 20)();

onUpgrade内でalterTableを呼び、columnTransformerでリネームするカラムの情報を記載します。

await m.alterTable(
  TableMigration(
    todos,
    columnTransformer: {
      todos.subtitle: const CustomExpression('title')
    },
  )
)

以上です。

参考

https://drift.simonbinder.eu/docs/advanced-features/migrations/

Discussion