4つのフィールドを持つモデルがあります。データベースから重複するオブジェクトを削除するにはどうすればよいですか?
この質問に対するDanielRosemanの答えは適切なようですが、オブジェクトごとに比較するフィールドが4つある状況にこれを拡張する方法がわかりません。
ありがとう、
W。
4つのフィールドを持つモデルがあります。データベースから重複するオブジェクトを削除するにはどうすればよいですか?
この質問に対するDanielRosemanの答えは適切なようですが、オブジェクトごとに比較するフィールドが4つある状況にこれを拡張する方法がわかりません。
ありがとう、
W。
def remove_duplicated_records(model, fields):
"""
Removes records from `model` duplicated on `fields`
while leaving the most recent one (biggest `id`).
"""
duplicates = model.objects.values(*fields)
# override any model specific ordering (for `.annotate()`)
duplicates = duplicates.order_by()
# group by same values of `fields`; count how many rows are the same
duplicates = duplicates.annotate(
max_id=models.Max("id"), count_id=models.Count("id")
)
# leave out only the ones which are actually duplicated
duplicates = duplicates.filter(count_id__gt=1)
for duplicate in duplicates:
to_delete = model.objects.filter(**{x: duplicate[x] for x in fields})
# leave out the latest duplicated record
# you can use `Min` if you wish to leave out the first record
to_delete = to_delete.exclude(id=duplicate["max_id"])
to_delete.delete()
頻繁に行うべきではありません。unique_together
代わりにデータベースの制約を使用してください。
id
これにより、DB で最大のレコードが残ります。元のレコード (最初のレコード) を保持したい場合は、 でコードを少し変更しますmodels.Min
。作成日など、まったく異なるフィールドを使用することもできます。
基礎となる SQL
django ORM に注釈を付ける場合GROUP BY
、クエリで使用されるすべてのモデル フィールドでステートメントを使用します。したがって、.values()
メソッドの使用。GROUP BY
これらの値が同一であるすべてのレコードをグループ化します。id
重複したもの (の複数) は、 on annotatedによって生成されたステートメントunique_fields
で後で除外されます。HAVING
.filter()
QuerySet
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()
念のため、.order_by()
a を集約する前に空の呼び出しを追加するのが常に賢明QuerySet
です。
注文に使用されるフィールドQuerySet
もGROUP BY
ステートメントに含まれています。.order_by()
モデルで宣言された空のオーバーライド列はMeta
、結果として SQL クエリに含まれません (たとえば、日付によるデフォルトの並べ替えは結果を台無しにする可能性があります)。
現時点ではオーバーライドする必要はないかもしれませんが、後で誰かがデフォルトの順序を追加し、それを知らずに貴重な重複削除コードを台無しにする可能性があります。はい、100% のテスト カバレッジがあると思います…</p>
.order_by()
安全のために空を追加するだけです。;-)
取引
もちろん、すべてを単一のトランザクションで行うことを検討する必要があります。
https://docs.djangoproject.com/en/3.2/topics/db/transactions/#django.db.transaction.atomic