【问题标题】:Django Queryset with filtering on reverse foreign key具有反向外键过滤功能的 Django Queryset
【发布时间】:2011-07-23 04:20:16
【问题描述】:

我有以下 Django 模型:

class Make:
   name = models.CharField(max_length=200)

class MakeContent:
   make = models.ForeignKey(Make)
   published = models.BooleanField()

我想知道我是否有可能(不直接编写 SQL)生成一个查询集,其中包含所有 Makes 和每个相关的 MakeContents 其中published = True

【问题讨论】:

  • 您的问题能否更具体一点?

标签: django model filter django-queryset


【解决方案1】:

是的,我想你想要

make = Make.objects.get(pk=1)
make.make_content_set.filter(published=True)

或许

make_ids = MakeContent.objects.filter(published=True).values_list('make_id', flat=True)
makes = Make.objects.filter(id__in=make_ids)

【讨论】:

  • 您的第一个代码 sn-p 不起作用。它获取 one make 的所有 MakeContents,其中需要所有 Makes 的 MakeContents。 _set 适用于单个对象,但不适用于查询集。
  • 添加flat = True 有什么意义?根据定义,ID 已经是唯一的,并且再次确保它们是唯一的可能需要一些额外的计算。
  • pintoch, flat=True 不提供与唯一性相关的任何内容。当只请求一个字段时,它只会返回单个值,而不是元组。
  • 我相信 make.make_content_set 在新版本的 Django 中应该是 make.makecontent_set(我使用的是 2.2)。
【解决方案2】:

我知道这是一个非常古老的问题,但我正在回答。因为我认为我的回答可以帮助其他人。我对模型进行了如下更改。我用过 Django 1.8。

class Make(models.Model):
    name = models.CharField(max_length=200)

class MakeContent(models.Model):
    make = models.ForeignKey(Make, related_name='makecontent')
    published = models.BooleanField()

我使用了以下查询集。

Make.objects.filter(makecontent__published=True)

希望它会有所帮助。

【讨论】:

  • 当有多个 MakeContent 指向同一个 Make 时,这将复制 Make 条目。像 select from make right join makecontent on make.id=makecontent.make_id 这样的东西,MakeContent 中的多行具有相同的 make_id
  • 你可以使用 Make.objects.filter(makecontent__published=True).distinct()
【解决方案3】:

Django 不支持使用select_related() 方法进行反向外键查找,因此不离开 Python 的最佳做法是两个数据库查询。第一个是抓取所有包含MakeContents where published = TrueMakes,第二个是抓取所有MakeContents where published = True。然后,您必须循环并按照您的需要排列数据。这是一篇关于如何做到这一点的好文章:

http://blog.roseman.org.uk/2010/01/11/django-patterns-part-2-efficient-reverse-lookups/

【讨论】:

  • 查看 prefetch_related() 方法以简化您提到的两个查询。
【解决方案4】:

让我将斯派克的措辞翻译成代码供未来的观众使用。请注意,每个“Make”可以有零到多个“MakeContent”

如果提问者的意思是用 至少一个“MakeContent”(其 published=True)来查询“Make”,那么 Jason Christa 的第二个 sn-p 会回答这个问题。

sn-p 等价于

makes = Make.objects.select_related().filter(makecontent__published=True).distinct()

但是,如果提问者的意思是用 ALL 'MakeContent' 查询'Make',它的published=True,那么按照上面的'makes',

import operator
make_ids = [m.id for m in makes if 
    reduce(operator.and_, [c.published for c in m.makecontent_set.all()] ) 
]
makes_query = Make.objects.filter(id__in=make_ids)

包含所需的查询。

【讨论】:

  • 我认为只有这个答案才符合问题
  • 这就是我想要的,但我想知道选择 all then distinct() 是否会降低性能
【解决方案5】:

还有一次,不清楚问题是什么,但如果希望所有相关的 MakeContent 对象都必须已发布,这可以工作:

Make.objects.exclude(MakeContent_set__published=False)

如果其中至少有一个(就像在其他答案中一样):

Make.objects.filter(MakeContent_set__published=True)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-05
    • 1970-01-01
    • 2021-07-02
    • 2021-12-24
    • 1970-01-01
    • 2019-07-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多