将Django模型迁移到unique_together约束

dzjeubhm  于 2022-12-30  发布在  Go
关注(0)|答案(2)|浏览(160)

我有一个有三个字段的模型

class MyModel(models.Model):
    a    = models.ForeignKey(A)
    b    = models.ForeignKey(B)
    c    = models.ForeignKey(C)

我想在这些字段之间强制一个唯一的约束,找到了django的unique_together,这似乎是一个解决方案.然而,我已经有一个现有的数据库,有很多重复.我知道,由于unique_together工作在数据库级别,我需要唯一化行,然后尝试迁移.
是否有一种好方法可以删除重复项(重复项具有相同的(A,B,C)),以便我可以运行迁移来获得unique_together约束?

ryevplcw

ryevplcw1#

如果你愿意任意选择一个副本,我想下面的方法可以解决这个问题。也许不是最有效的,但是足够简单,我猜你只需要运行一次。请验证一下,如果我做了一些愚蠢的事情,你自己的一些测试数据,因为你要删除一堆数据。
首先,我们找到形成副本的对象组。对于每个组,(任意地)选择我们要保留的“主对象”。我们选择的方法是选择具有最小pk的对象

from django.db.models import Min, Count

master_pks = MyModel.objects.values('A', 'B', 'C'
    ).annotate(Min('pk'), count=Count('pk')
    ).filter(count__gt=1
    ).values_list('pk__min', flat=True)

然后,我们循环遍历每个主文件,并删除其所有副本

masters = MyModel.objects.in_bulk( list(master_pks) )

for master in masters.values():
    MyModel.objects.filter(a=master.a, b=master.b, c=master.c
        ).exclude(pk=master.pk).del_ACCIDENT_PREVENTION_ete()
7vhp5slm

7vhp5slm2#

我想添加一个稍微改进的答案,它将删除单个查询中的所有内容,而不是循环并删除每个重复的组。如果您有很多记录,这将快得多。

non_dupe_pks = list(
    Model.objects.values('A', 'B', 'C')
    .annotate(Min('pk'), count=Count('pk'))
    .order_by()
    .values_list('pk__min', flat=True)
)

dupes = Model.objects.exclude(pk__in=non_dupe_pks)
dupes.delete()

在第一个查询中添加order_by()非常重要,否则模型中的默认排序可能会扰乱聚合。
您可以注解掉最后一行,然后使用dupes.count()检查查询是否按预期工作。

相关问题