一部の行が誤って作成された以前の不良な移行を修正するために実行しているかなり長いデータ移行があります。古い列に基づいて新しい列に値を割り当てようとしていますが、整合性エラーが発生することがあります。これが発生した場合、整合性エラーの原因となっているものを破棄したい
ここにコードスニペットがあります:
def load_data(apps, schema_editor):
MyClass = apps.get_model('my_app', 'MyClass')
new_col_mapping = {old_val1: new_val1, ....}
for inst in MyClass.objects.filter(old_col=c):
try:
inst.new_col = new_col_mapping[c]
inst.save()
except IntegrityError:
inst.delete()
次に、私のMigration
クラスの操作で
operations = [
migrations.RunPython(load_data)
]
移行を実行すると、次のエラーが表示されます
Django.db.transaction.TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block
やっている感じがする
with transaction.atomic():
どこかに私の解決策がありますが、正しい場所がどこにあるのか正確にはわかりません。さらに重要なのは、これが必要な理由を理解したい
これは ドキュメントの例 に似ています。
まず、必要なインポートがまだない場合は追加します。
from Django.db import transaction
次に、整合性エラーを発生させる可能性のあるコードをアトミックブロックでラップします。
try:
with transaction.atomic():
inst.new_col = new_col_mapping[c]
inst.save()
except IntegrityError:
inst.delete()
エラーの理由は、警告ブロック「アトミック内で例外をキャッチしないでください!」で説明されています。ドキュメントで。 Django=データベースエラーが発生すると、アトミックブロックがロールバックされます。これ以上データベースクエリを試行すると、表示されているTransactionManagementError
が発生します。コードをアトミックブロックでは、そのコードのみがロールバックされ、ブロックの外部でクエリを実行できます。
各移行は1つのトランザクションにラップされるため、移行中に何かが失敗すると、すべての操作がキャンセルされます。そのため、何かが失敗した各トランザクションは、新しいクエリを受け取ることができません(とにかくキャンセルされます)。
with transaction.atomic():
を使用して一部の操作をラップすることは良い解決策ではありません。何かが失敗したときにその操作をキャンセルすることができないからです。その代わりに、データを保存する前にいくつかのチェックを実行して、整合性エラーを回避します。
同じ例外にはさまざまな原因が考えられます。私の場合は、無効なモデルフィールド名が原因でした。フィールド名にギリシャ文字のデルタ????
を使用しました。
それは正常に機能しているように見え、すべてのアプリが適切に機能しました(おそらく、これ以上複雑な使用例を試さなかっただけかもしれません)。ただし、テストではTransactionManagementError
が発生しました。
フィールド名とすべての移行ファイルから????
を削除することで問題を解決しました。