web-dev-qa-db-ja.com

Djangoにいくつかのテーブルが既に存在する場合、DBへの移行を強制する方法は?

Python/Djangoプロジェクトがあります。いくつかのロールバック、およびその他のさまざまな問題のために、一種の奇妙なシナリオに陥りました。

現在のシナリオは次のとおりです。

  • DBには正しいテーブルがあります
  • DBはロールバックまたはドロップできません
  • コードは最新です

  • 移行フォルダーはbehind 1つまたは2つの移行によるDBです。 (これらの移行は別の場所から適用され、その「別の場所」はもう存在しません)

  • 一部のモデルを追加および変更します

  • makemigrationsを実行します
  • 新しい移行が作成されますが、新しいテーブルと、DBにすでに存在するいくつかのテーブルが混在しています。
  • migrateを実行すると、作成しようとしているテーブルの一部が既に存在していると文句を言うでしょう。

必要なもの:

移行を実行し、既存のテーブルを「無視」して、新しいテーブルを適用できるようにするため。または、これを達成するための代替方法。

それは可能ですか?

16

移行を適用すると、DjangoはDjango_migrationsというテーブルに行を挿入します。これが、Djangoが、どの移行が既に適用され、どの移行が適用されていないかを知る唯一の方法です。そのため、そのテーブルの行は、migrationsディレクトリ内のファイルと一致する必要があります。適用後に移行ファイルを紛失したり、他のものを同期させない場合、問題が発生します。データベース内の移行番号は、プロジェクト内の移行ファイルとは異なる移行ファイルを参照しているためです。

そのため、他の作業を行う前に、何らかの理由で失われ、元に戻せない移行ファイルのDjango_migrationsテーブル行を削除して、同期を取り戻す必要があります。 テーブルには、実際にデータベースに正しく適用された移行のみの行が含まれている必要があります

ここで、Django Migrationsが認識しないデータベースの変更に対処する必要があります。そのためのオプションがいくつかあります。

既にデータベースに適用されているデータベースの変更がそうでないものとは異なる移行ファイルにあるように物事がうまくいった場合、--fakeオプションを使用して移行を1つずつ実行することで修正できます実際にデータベースに既に存在する変更について。偽のオプションは、行をDjango_migrationsテーブルに書き込むだけで、移行が完了したことを示します。データベースに実際にその移行ファイルに含まれるすべての変更が既にある場合にのみ、これを行います。

また、データベースに適用されていない変更のみを含む移行ファイルは、--fakeオプションなしで実行し、Djangoを適用します。例えば:

# database already has it
manage.py migrate myapp 0003 --fake 
# need it
manage.py migrate myapp 0004
# database already has it
manage.py migrate myapp 0005 --fake

すべてではないが一部の変更が適用された移行ファイルがある場合、より大きな問題が発生します。その場合は、いくつかの方法があります(1つだけ選択してください)。

  1. 移行ファイルを編集して、既に適用されている変更(Djangoが行ったか、手動で行ったかは関係ありません)を小さい番号の移行に入れ、必要なすべてを大きい番号のファイルに入れます。これで、小さい番号の--fakeを使用し、大きい番号の番号を通常どおり実行できます。モデルに10個の変更があり、そのうち5個は既にデータベースにあるが、Djangoはそれらについて知らないとします。したがって、makemigrationsを実行すると、 10個すべての変更を含む新しい移行が作成されます。データベースサーバーは、たとえば既に存在する列を追加できないため、これは通常失敗します。これらの既に適用された変更を新しい移行ファイルから以前の(既に適用された)移行ファイルに移動します。 Djangoは、これらが以前の移行で適用されたと想定し、再度適用しようとはしません。その後、通常どおりmigrateすると、新しい変更が適用されます。

    古い移行ファイルを変更したくない場合は、最初にmakemigrations --empty appnameを実行して空の移行ファイルを作成するのがよりクリーンな方法です。次に、Djangoが実行する必要があると考えるすべての変更を含む別の移行を作成するmakemigrationsを実行します。そのファイルから既に作成した移行を、作成した空の移行に移動します..--fakeその移行。これにより、データベースがどのように見えるかをDjangoが理解し、現実と同期し、migrateを通常どおりに使用して、最後の移行ファイルに変更を適用できます。

  2. Makemigrationsを使用して作成したばかりの新しい移行を取り除きます。ここで、データベースに適用されていないモデルのすべてをコメントアウトするか、元に戻します。コードは、データベースに実際にあるものと一致したままになります。これでmakemigrationsmigrate appname --fakeを実行でき、同期を取り戻すことができます。次に、新しいコードのコメントを解除し、「makemigrations」を実行してから通常どおりmigrateを実行すると、変更が適用されます。変更が小さい場合(たとえば、いくつかのフィールドを追加する場合)、これが最も簡単な場合があります。変更が大きい場合は、そうではありません。

  3. 先に進んで、(慎重に)データベースを変更して、データベースを最新の状態にすることができます。ここでmigrate --fakeを実行するだけで、混乱していなければすべて問題ありません。繰り返しますが、これは小さな変更では簡単ですが、複雑な変更ではそれほど簡単ではありません。

  4. manage.py sqlmigrate > mychanges.sqlを実行できます。これにより、データベースに対して実行されたすべてのSQL Django WOULDを含むmychanges.sqlが生成されます。次に、そのファイルを編集して、すでに適用されている変更を削除し、実行する必要があるものを残します。 pgadminまたはpsqlを使用してそのSQLを実行します(postgresqlを使用していると思います)。これで変更がすべて行われたので、manage.py migrate --fakeを実行できます。これによりDjangoが現実と同期し、すべての設定が完了します。 SQLのスキルが十分であれば、これはおそらく最も簡単なソリューションです。

2つの警告を追加する必要があります。

最初に、後の移行、たとえば0003_foobar.pyを適用し、その後うまくいかない場合、戻って0002_bazbuz.pyを適用しようとすると、Djangoはデータベースからデータを削除します。たとえば、0003で追加した列は、そのデータとともに削除されます。あなたはデータを失うことはできないと言っているので、戻ることに非常に注意してください。

第二に、--fake移行の実行を急がないでください。偽造しようとしている移行全体が、実際に既にデータベースにあることを確認してください。そうでなければ、非常に混乱します。フェイク移行を後悔し、ロールバックしたくない場合、Django_migrationsテーブルからその行を削除することにより、フェイク移行に関するDjangoの知識を消去できます。これを行っても大丈夫です。あなたが何をしているかを理解しているなら。移行が実際に適用されなかったことがわかっている場合は、問題ありません。

32
little_birdie

このブログの投稿は本当に釘付けです。 https://simpleisbetterthancomplex.com/tutorial/2016/07/26/how-to-reset-migrations.html

彼のシナリオ2の手順を要約しましょう(運用データベースがあり、1つ以上のアプリでスキーマ/モデルを変更したい)。私の場合、キューとルーティングスリップという2つのアプリがあり、それらには運用システムに適用するために必要なモデル変更がありました。重要なのは、すでにデータベースを持っているため、これが--fake-initialの出番です。

ここに私が従った手順があります。いつものように、開始する前にすべてをバックアップしてください。私はVMで作業しているので、先に進む前にスナップショットを撮りました。

1)各アプリの移行履歴を削除します。

python manage.py migrate --fake queue zero
python manage.py migrate --fake routingslip zero

2)アプリが存在するプロジェクト全体の移行ファイルを吹き飛ばします

find . -path "*/migrations/*.py" -not -name "__init__.py" -delete
find . -path "*/migrations/*.pyc"  -delete

3)移行を行う

python manage.py makemigrations

4)移行を適用します。データベースが既に存在し、変更が必要なため、最初は偽装します。

python manage.py migrate --fake-initial

私にとっては素晴らしい仕事でした。

2
slogan621