【Flutter】Driftのマイグレーション
はじめに
Driftのマイグレーションについての記事になります。
DBに加える変更によってマイグレーション時の処理がところどころ変わってくるので、最初に基本の流れを説明してから各変更に対応したマイグレーション時の処理について記載していきます。
今回使用した環境は以下になります。
Flutter 3.7.12
Drift 2.7.0
基本の流れ
カラムの追加を通して、基本的なマイグレーションの流れを見ていきます。
以下の流れで説明していきます。
- テーブル修正
- DBクラス修正
- コード生成
1. テーブル修正
今回は下記のid
とtitle
を持つテーブルに新たに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未満の場合にこのマイグレーション処理が行われるように制御を入れます。また、今回はカラムを追加するのでMigrator
のaddColumn()
メソッドを使用します。
@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
カラムが追加されていることと、デフォルト値が設定されていることを確認できました。
マイグレーション前 | マイグレーション後 |
---|---|
その他のマイグレーション処理
ここからは、上記の基本の流れで使用したコードを元に、他の操作を行う場合のマイグレーション処理についていくつか記載していきます。
テーブル追加・削除
テーブルの追加・削除はMigrator
のcreateTable()
, 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);
+ }
},
);
}
}
カラム削除・制約変更
カラム削除・制約変更はMigrator
のalterTable()
メソッドで行うことができます。
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));
カラムのリネーム
カラムのリネームにはいくつか方法があるのでそれぞれ紹介していきます。
named()
1. 実際のDB上のカラム名を変える必要がない場合は、named()
を使用することでマイグレーション無しでリネームが可能のようです。
コード上では変更後のカラム名で扱うことができるようになります。
title
をsubtitle
にリネームする例を載せます。
- TextColumn get title => text().withLength(min: 10, max: 20)();
+ TextColumn get subtitle => text().named('title').withLength(min: 10, max: 20)();
Migrator
のrenameColumn
2. アプリがsqlite 3.25.0以降で動作することがわかっている場合は、Migrator
のrenameColumn
を使用することができます。
まず、テーブルのtitle
をsubtitle
にリネームする修正を加えます。
- 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);
columnTransformer
3. 2と同様、まずテーブルのtitle
をsubtitle
にリネームする修正を加えます。
- 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')
},
)
)
以上です。
参考
Discussion