如何在两个字段中找到django模型的重复项?

izkcnapc  于 2023-04-13  发布在  Go
关注(0)|答案(3)|浏览(162)

考虑以下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'
3zwjbxry

3zwjbxry1#

可以同时使用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)
57hvy0tb

57hvy0tb2#

你的手工方式也是低效的。你可以通过使用以下命令在O(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 select only rows with duplicate field values

1cklez4t

1cklez4t3#

假设你的问题中有一个错别字(有一个f而不是b,你想要的是任何对fbfb = 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)如果你想知道每对值fb有多少个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)

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

相关问题