Django ORM:选择相关集
Posted
技术标签:
【中文标题】Django ORM:选择相关集【英文标题】:Django ORM: Selecting related set 【发布时间】:2009-05-12 14:50:25 【问题描述】:假设我有 2 个模型:
class Poll(models.Model):
category = models.CharField(u"Category", max_length = 64)
[...]
class Choice(models.Model):
poll = models.ForeignKey(Poll)
[...]
给定一个 Poll 对象,我可以查询它的选择:
poll.choice_set.all()
但是,是否有一个实用函数可以从一组 Poll 中查询所有选项?
实际上,我正在寻找类似以下的东西(不支持,我也不寻求它是如何实现的):
polls = Poll.objects.filter(category = 'foo').select_related('choice_set')
for poll in polls:
print poll.choice_set.all() # this shouldn't perform a SQL query at each iteration
我制作了一个(丑陋的)函数来帮助我实现这一目标:
def qbind(objects, target_name, model, field_name):
objects = list(objects)
objects_dict = dict([(object.id, object) for object in objects])
for foreign in model.objects.filter(**field_name + '__in': objects_dict.keys()):
id = getattr(foreign, field_name + '_id')
if id in objects_dict:
object = objects_dict[id]
if hasattr(object, target_name):
getattr(object, target_name).append(foreign)
else:
setattr(object, target_name, [foreign])
return objects
具体用法如下:
polls = Poll.objects.filter(category = 'foo')
polls = qbind(polls, 'choices', Choice, 'poll')
# Now, each object in polls have a 'choices' member with the list of choices.
# This was achieved with 2 SQL queries only.
Django 是否已经提供了一些更简单的东西?或者至少,一个 sn-p 以更好的方式做同样的事情。
你通常如何处理这个问题?
【问题讨论】:
也许你的 qbind 函数是可以做到的最好的。但将其打包在自定义管理器中可能是有意义的 - docs.djangoproject.com/en/dev/topics/db/managers/#id2 【参考方案1】:时间已经过去了,随着 prefetch_related() QuerySet 函数的引入,这个功能现在在 Django 1.4 中可用。此函数有效地执行建议的qbind
函数执行的操作。 IE。执行了两个查询,连接发生在 Python 领域,但现在由 ORM 处理。
原来的查询请求现在变成:
polls = Poll.objects.filter(category = 'foo').prefetch_related('choice_set')
如以下代码示例所示,polls
QuerySet 可用于获取每个Poll
的所有Choice
对象,而无需任何进一步的数据库命中:
for poll in polls:
for choice in poll.choice_set:
print choice
【讨论】:
+1 进行跟进。谷歌偶然发现此页面的任何人的最佳解决方案。 我使用 Django 1.6,并且在 python shell 中使用时得到类型错误相关的管理器对象不可迭代。我做了和 Frederic 完全相同的事情:Choice 有外键投票,所以两者之间存在 1 到 n 的关系。民意调查和选择 @Timo 你应该打电话给poll.choice_set.all()
。【参考方案2】:
更新:从 Django 1.4 开始,此功能已内置:请参阅prefetch_related。
第一个答案:不要浪费时间编写类似 qbind 的东西,除非您已经编写了一个工作应用程序,对其进行了概要分析,并证明 N 次查询实际上是您的数据库和负载方案的性能问题。
但也许你已经做到了。所以第二个答案: qbind() 可以完成您需要做的事情,但是如果将其打包在自定义 QuerySet 子类中,并附带一个返回自定义 QuerySet 实例的 Manager 子类,则它会更加惯用。理想情况下,您甚至可以使它们通用且可重用于任何反向关系。然后你可以这样做:
Poll.objects.filter(category='foo').fetch_reverse_relations('choices_set')
有关 Manager/QuerySet 技术的示例,请参阅this snippet,它解决了类似的问题,但针对通用外键的情况,而不是反向关系。将 qbind() 函数的内容与此处显示的结构结合起来,为您的问题提供一个非常好的解决方案并不难。
【讨论】:
【参考方案3】:我认为您的意思是,“我想要一组民意调查的所有选项。”如果是这样,试试这个:
polls = Poll.objects.filter(category='foo')
choices = Choice.objects.filter(poll__in=polls)
【讨论】:
+1 我不知道这个功能!多么优雅! 这是我在qbind
函数开始时所做的。但实际上我想要一组选择 per 民意调查,而不是整套选择。例如,如果我想在模板上显示民意调查列表,以及每个人的选择,我不想为每个民意调查访问数据库。 qbind
函数的重点是将您的 polls
和 choices
数据连接在一起以实现这一目标。【参考方案4】:
我认为您正在尝试做的是术语“急切加载”子数据 - 这意味着您正在为每个轮询加载子列表 (choice_set),但所有这些都在对数据库的第一个查询中,因此您不需要以后不必进行大量查询。
如果这是正确的,那么您正在寻找的是 'select_related' - 请参阅 https://docs.djangoproject.com/en/dev/ref/models/querysets/#select-related
我注意到您尝试了“select_related”,但没有成功。你可以尝试做'select_related'然后过滤。这可能会解决它。
更新:这不起作用,请参阅下面的 cmets。
【讨论】:
select_related 如果 a 正在查询 Choice 并希望预加载每个相应的民意调查,这将很有用。但是在这里,我想要 select_related 不支持的相反(想想相应的 SQL 查询,如果不复制大量数据,它不能在一个查询中完成。)这应该用 2 个查询来完成。 是的,你的权利。很抱歉没有看到。由于 'choice_set' 在查询被评估之前不可用,因此它甚至无法识别它的存在。以上是关于Django ORM:选择相关集的主要内容,如果未能解决你的问题,请参考以下文章
使用 Django 的 ORM 和 Django Rest Framework 序列化嵌套关系的查询集的正确方法?