如何在两个字段上找到django模型的副本?



考虑这样的django模型设置:

from django.db import models
class Foo(models.Model):
field_foo = models.CharField(max_length=20,
null=False,
blank=False,
unique=True,)
class Bar(models.Model):
field_bar = models.CharField(max_length=20,
null=False,
blank=False,
unique=True,)
class Foobar(models.Model):
field_foo = models.ForeignKey(foo,on_delete=models.CASCADE)
field_bar = models.ForeignKey(bar,on_delete=models.CASCADE)

我想查找具有相同field_foofield_bar值的两行。我可以手动做到这一点,但我想知道是否有一个功能的django,照顾这一点。我现在的做法是:

for f in Foo.objects.all():
for b in Bar.objects.all():
fb = Foobar.objects.filter(foo=f, bar=b)
if len(fb)>2:
something='do'

你可以同时使用annotate()和filter()方法来过滤Foo和Bar实例作为关系的FooBar实例。

试一试

duplicate_foobars = Foobar.objects.values('id', 'field_foo', 'field_bar').annotate(
count=Count('id')
).filter(
count__gt=1
).values_list('id', 'field_foo', 'field_bar')
for id, field_foo, field_bar in duplicate_foobars:
print(f"Duplicate Foobar: #{id} field_foo={field_foo}, field_bar={field_bar}")

输出
Duplicate Foobar: #1, field_foo=1, field_bar=1

测试模型
class Foo(models.Model):
field_foo = models.CharField(
max_length=20,
null=False,
blank=False,
unique=True,
)

class Bar(models.Model):
field_bar = models.CharField(
max_length=20,
null=False,
blank=False,
unique=True,
)

class Foobar(models.Model):
field_foo = models.ForeignKey(Foo, on_delete=models.CASCADE)
field_bar = models.ForeignKey(Bar, on_delete=models.CASCADE)

你的手工方式也是低效的。您可以在0 (n)内使用:

foo_bar_count = {}
for fb in Foobar.objects.all():
foo_bar_pair = (fb.field_foo_id, fb.field_bar_id)

if foo_bar_pair not in foo_bar_count:
foo_bar_count[foo_bar_pair] = 0
continue
foo_bar_count[foo_bar_pair] += 1
if foo_bar_count[foo_bar_pair] > 2:
# do something

您也可以尝试使用一些奇特的查询来执行此操作。检查:Django只选择字段值重复的行

假设您的问题中有错字(有f而不是b,您想要的是任何一对f,bfb = Foobar.objects.filter(foo=f, bar=b)替代品),这里有一个提示:

queryset = (
Foobar.objects
.values("field_foo", "field_bar")
.annotate(nb=models.Count("id"))
.filter(nb__gt=1)
)

实际上,最优的方法取决于你想要达到的目标。

(1)如果你想知道每一对值f,b存在多少个Foobar实例:

foobars = queryset.values("field_foo__field_foo", "field_bar__field_bar", "nb")

(2)或者如果您想检索所有相应的实例,您可以这样做:

from django.db.models import Q
any_of = (
Q(field_foo__field_foo=x, field_bar__field_bar=y)
for x, y in queryset.values_list("field_foo__field_foo", "field_bar__field_bar")
)
criteria = Q()
for q_obj in any_of:
criteria |= q_obj
Foobar.objects.filter(criteria)

可能也有一种方法可以使用子查询来做同样的事情,请查看文档。

最新更新