2012-12-04 5 views
21

私は4つのフィールドを持つモデルを持っています。データベースから重複したオブジェクトを削除するにはどうすればよいですか?Django - 複数のフィールドがある場合は重複するオブジェクトを削除します

this questionのDaniel Rosemanさんの回答は適切ですが、これをオブジェクトごとに比較する4つのフィールドがある状況に拡張する方法がわかりません。

おかげで、あなたは多くの場合、それを行うべきではありません

答えて

56
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/aggregation/#interaction-with-default-ordering-or-order-by

トランザクション

あなたは、単一のトランザクションですべてを行うことを検討すべきです。

https://docs.djangoproject.com/en/1.11/topics/db/transactions/#django.db.transaction.atomic

+0

ありがとうございます!しかし、私は(私はまだ非常にDjangoの初心者です)理解することができるように、あなたはそれぞれのステップで何が起こっているか説明できますか?私は 'MyModel.objects.values(* unique_fields)'はオブジェクトに関する各辞書を使って辞書のセットを生成することを理解しています。しかし、私は失われます - 注釈は何をしていますか? – Westerley

+1

私の更新が少し物事を明確にすることを願っています。 –

+1

ブリリアント!完璧に動作します!私にはかなりの研究と思考がありました** **どのように動作するのか(あなたの説明はかなり助けになり、私が読んでいたものを理解するのを助けました。もう一度お礼を言います(そして、これに戻ることの遅れのためにお詫び申し上げます) – Westerley

関連する問題