Djangoのデータベースルーターのallow_migrateの挙動について

Djangoフレームワークでは、データベースルータークラスを作成して、 settings.DATABASE_ROUTERS に設定することで、モデル毎に使用するデータベースを変えたりできます。

データベースルーターにはいくつかのメソッドを実装しますが、その中の一つ、 allow_migrate を実装した際のマイグレーション処理の挙動について気になったので調べました。

試した環境は、Python3.7.1、Django 2.1.3。

前提

  • Djangoのプロジェクト名: project1
  • アプリケーション名: myapp

対象のモデルは以下の通り:

myapp/models.py:

from django.db import models

class Spam(models.Model):
    name = models.CharField(max_length=20)

この Spam モデルをデータベースルーターにて、マイグレーションの対象外とします。

データベースルーターは以下の通り:

project1/db_router.py:

class DatabaseRouter:
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if app_label == 'myapp':
            return False
        return None

settings.pyDATABASE_ROUTERS に設定しています。

project1/settings.py (抜粋):

DATABASE_ROUTERS = [
    'project1.db_router.DatabaseRouter',
]

makemigrationsを実行した場合

マイグレーションファイルを作成する makemigrations コマンドを実行した場合は、 allow_migrateFalse を返却しても、マイグレーションファイルは作成されます。

$ python manage.py makemigrations myapp
Migrations for 'myapp':
  myapp/migrations/0001_initial.py
    - Create model Spam

migrateを実行した場合

マイグレーションファイルを作成した状態で、マイグレーションを実行する migrate コマンドを実行した場合は、対象のマイグレーションはスキップされます。

$ python manage.py showmigrations myapp
myapp
 [ ] 0001_initial

$ python manage.py migrate myapp
Operations to perform:
  Apply all migrations: myapp
Running migrations:
  Applying myapp.0001_initial... OK

$ python manage.py showmigrations myapp
myapp
 [X] 0001_initial

このとき、Djangoマイグレーションの管理情報を格納するテーブルには、マイグレーションの実行済みを表すレコードが作成されます。

$ sqlite3 db.sqlite3 -line "select * from django_migrations"
     id = 1
    app = myapp
   name = 0001_initial
applied = 2018-11-20 20:59:28.637569

注意したい点

  • allow_migrateFalse を返しても、 migrate ではマイグレーションファイルがあればマイグレーションの管理テーブルには書き込まれる
    • 意図せず対象のデータベースに django_migrations データベースが作成されたり、レコードが書き込まれることがある
    • 管理情報を書き込みたくない場合は、モデルに Meta.managed = False を指定し、管理対象外にしておく必要がある

参考