私は4つのフィールドを持つモデルを持っています。データベースから重複したオブジェクトを削除するにはどうすればよいですか?Django - 複数のフィールドがある場合は重複するオブジェクトを削除します
this questionのDaniel Rosemanさんの回答は適切ですが、これをオブジェクトごとに比較する4つのフィールドがある状況に拡張する方法がわかりません。
おかげで、あなたは多くの場合、それを行うべきではありません
私は4つのフィールドを持つモデルを持っています。データベースから重複したオブジェクトを削除するにはどうすればよいですか?Django - 複数のフィールドがある場合は重複するオブジェクトを削除します
this questionのDaniel Rosemanさんの回答は適切ですが、これをオブジェクトごとに比較する4つのフィールドがある状況に拡張する方法がわかりません。
おかげで、あなたは多くの場合、それを行うべきではありません
unique_fields = ['field_1', …, 'field_n']
duplicates = (MyModel.objects.values(*unique_fields)
.order_by()
.annotate(max_id=models.Max('id'),
count_id=models.Count('id'))
.filter(count_id__gt=1))
for duplicate in duplicates:
(MyModel.objects.filter(**{x: duplicate[x] for x in unique_fields})
.exclude(id=duplicate['max_id'])
.delete())
W.。代わりにunique_together
データベースの制約を使用します。
基礎となるSQLコード
DjangoのORMに注釈を付けることは、クエリで使用されるすべてのモデルフィールド上GROUP BY
ステートメントを使用しています。従って、.values()
方法の使用。 GROUP BY
は、これらの値を持つすべてのレコードをグループ化します。複製されたもの(のunique_fields
)は、後でのQuerySet
に生成されたHAVING
文で除外されます。
SELECT
field_1,
…
field_n,
MAX(id) as max_id,
COUNT(id) as count_id
FROM
app_mymodel
GROUP BY
field_1,
…
field_n
HAVING
count_id > 1
重複レコードが後、各グループのための最も頻繁なものと例外とfor
ループに削除されます。
空.order_by()
念のために、それはQuerySet
を集約する前に、空の.order_by()
呼び出しを追加することは常に賢明です。
QuerySet
の注文に使用されるフィールドは、GROUP BY
ステートメントにも含まれています。空の.order_by()
は、モデルのMeta
で宣言された列をオーバーライドし、その結果、SQLクエリには含まれません(たとえば、日付によるデフォルトの並べ替えは結果を破損する可能性があります)。
現時点でそれを上書きする必要はないかもしれませんが、誰かがデフォルトの順序を後で追加して、それを知っていない貴重な削除複製コードを壊すことがあります。はい、私はあなたが100%のテストカバレッジを持っていると確信しています...
ただ.order_by()
を安全に追加してください。もちろん;-)
トランザクション
あなたは、単一のトランザクションですべてを行うことを検討すべきです。
https://docs.djangoproject.com/en/1.11/topics/db/transactions/#django.db.transaction.atomic
ありがとうございます!しかし、私は(私はまだ非常にDjangoの初心者です)理解することができるように、あなたはそれぞれのステップで何が起こっているか説明できますか?私は 'MyModel.objects.values(* unique_fields)'はオブジェクトに関する各辞書を使って辞書のセットを生成することを理解しています。しかし、私は失われます - 注釈は何をしていますか? – Westerley
私の更新が少し物事を明確にすることを願っています。 –
ブリリアント!完璧に動作します!私にはかなりの研究と思考がありました** **どのように動作するのか(あなたの説明はかなり助けになり、私が読んでいたものを理解するのを助けました。もう一度お礼を言います(そして、これに戻ることの遅れのためにお詫び申し上げます) – Westerley