【问题标题】:how to efficiently use _set.all() in django?如何在 django 中有效地使用 _set.all()?
【发布时间】:2016-06-23 23:59:08
【问题描述】:

当 Entry 对象的数量大于 5000 个条目时,django 中有没有办法更有效地执行以下操作?

models.py

class Entry(models.Model):
    user = models.TextField(db_column='User', blank=True)
    date = models.DateTimeField(blank=True)

class Color(models.Model):
    color = models.TextField(blank=True)
    entry = models.ForeignKey(Entry)

假设我想获取每个条目的所有颜色...

entrys = Entry.objects.all()

for e in entrys:
    print e.color_set.all()

我希望能够将每个对象与特定条目相关联。例如,在这样的 csv 表中。

user, color
john, blue
john, orange
bob, green
bob, red
bob, purple

浏览我的所有条目需要几秒钟的时间。有没有更好的办法?

【问题讨论】:

  • 你可以换一种方式。获取所有 Color 对象并按entry 排序,然后迭代。
  • 你可以按照an answer I just made on a different question使用prefetch_related
  • 要扩展@Sayse 评论,selected_related 也可以是一个选项。 docs.djangoproject.com/en/1.9/ref/models/querysets/… 你基本上是想做一个急切的加载。
  • @themanatuf - select_related 不是一个选项,因为它不适用于一对多
  • @Sayse 是的,你是对的,但如果原始海报遵循@AKS 的建议,那么select_related 会起作用。

标签: python django optimization reverse-lookup


【解决方案1】:

你应该使用prefetch_related

entrys = Entry.objects.all().prefetch_related('color_set')

for e in entrys:
    print e.color_set.all()

查询它将执行 2 次,而不是 n 次,一次用于条目,一次用于外键查找

【讨论】:

  • 这似乎运行得并不快...我错过了什么吗?
  • @JohnWaller - 我不确定,时间问题可能不再在数据库方面,但可能只需要一段时间来打印出值或任何问题的 csv 部分指的是
  • @JohnWaller - 当然,您仍在尝试从数据库中返回大量记录,因此肯定需要一些时间
【解决方案2】:

正如我之前评论的,如果你只需要一个Entry 的所有颜色,你可以选择所有Color 对象并在entry 上排序:

colors = Color.objects.order_by('entry')

现在,您可以遍历这些对象并以您想要的方式打印它们:

for color in colors:
    print(color.entry.user, color.color)

# john, blue
# john, orange
# bob, green

或者,您可以将此信息提取为values_list

color_entries = list(colors.values_list('entry__user', 'color'))
# [('john', 'blue'), ('john', 'orange'), ('bob', 'green'), ...]

【讨论】:

  • 如果我有另一个也与 entry 相关的模型并且我也想要这些值怎么办?
  • 这仍将创建 n (5000) 个查询,因为它将在每次迭代时检索 entry
  • 但是如果你使用values_list,它会很有效。或者,我是否缺少values_list 的内容?
  • 由于不同的原因它会更有效,因为它将选择某些值而不是*
  • 我同意。而且,我更喜欢你的回答。我只是想提供一种替代方法并将其限制为问题中提供的示例。
最近更新 更多