原因
この循環依存エラー**"CircularDependencyError"**は、異なるアプリケーションのマイグレーションファイルがお互いのモデルを外部参照している場合に発生する模様。
エラー例
二つのアプリ"app1","app2"でお互いのモデルをForeignKeyでリレーションしているとき
app1/models.py
from django.db import models
class model_A(models.Model):
id = models.AutoField( primary_key=True )
class model_B(models.Model):
id = models.AutoField( primary_key=True )
app2_a_model = models.ForeignKey('app2.model_A', on_delete=models.CASCADE)
app2/models.py
from django.db import models
class model_A(models.Model):
id = models.AutoField( primary_key=True )
class model_B(models.Model):
id = models.AutoField( primary_key=True )
app1_a_model = models.ForeignKey('app1.model_A', on_delete=models.CASCADE)
makemigraionsを実行すると
python manage.py makemigrations app1
python manage.py makemigrations app2
お互いに依存しあったマイグレーションをファイルが出来上がり
app1/migrations/0001_initial.py
...
dependencies = [
('app2', '0001_initial'),
]
...
app2/migrations/0001_initial.py
...
dependencies = [
('app1', '0001_initial'),
]
...
CircularDependencyErrorが発生する。
django.db.migrations.exceptions.CircularDependencyError: app1.0001_initial, app1.0001_initial
解決策
アプリの役割を整理して循環依存状態を解消するのが良いかも知れないが、ここでは循環依存状態のままマイグレーションを成功させる方法を模索。
一つ分かっている事は、アプリ同士のモデルが依存し合っていても、依存先のマイグレーション世代が異なればCircularDependencyErrorは発生しない。
そこで一時的に循環依存状態を解消してマイグレーションファイルを作成してから循環依存状態に戻し次の世代のマイグレーションファイルを作成する事に。
手順としては、
- app1のmodel_Bモデルのリレーション先を'app2.model_A'からダミーモデル'Dummy_model'に変更してapp1 をmakemigraions
- app2をmakemigraions
- app1のmodel_Bモデルのリレーション先を元に戻してapp1をmakemigraions
# 循環依存解消
cp -pi app1/models.py app1/models.py.ori
sed "s/'app2.model_A'/'Dummy_model'/" app1/models.py.ori > app1/models.py
# ダミーモデルを追記
cat >>app1/models.py <<'__EOF__'
class Dummy_model(models.Model):
id = models.AutoField ( primary_key=True )
__EOF__
python manage.py makemigrations app1
python manage.py makemigrations app2
# app1のmodel_Bモデルのリレーション先を元に戻す。
mv -f app1/models.py.ori app1/models.py
python manage.py makemigrations app1
# マイグレーションを実行
python manage.py migrate
これでmigrateを実行するとエラーが解消される。
makemigration実行時に、特定フィールドやテーブルだけ除外する方法は、無かった。
もっとスマートな方法はないだろうか?