【问题标题】:Django: Can you tell if a related field has been prefetched without fetching it?Django:你能判断一个相关字段是否已经被预取但没有获取它吗?
【发布时间】:2013-10-29 16:21:30
【问题描述】:

我想知道 Django 中是否有一种方法可以判断相关字段,特别是一对多关系的“多”部分是否已通过 prefetch_related() 获取,而实际上并未获取它?

因此,举个例子,假设我有这些模型:

class Question(Model):
  """Class that represents a question."""

class Answer(Model):
  """Class the represents an answer to a question."""
  question = ForeignKey('Question', related_name='answers')

通常,要获得一个问题的答案数量,最有效的方法是执行以下操作(因为 Django 文档指出,如果您只需要计数,count() 会更有效):

# Note: "question" is an instance of class Question.
answer_count = question.answers.count()

但是,在某些情况下,答案可能是通过prefetch_related() 调用(或某种方式,例如之前对答案进行迭代)获取的。所以在这种情况下,这样做会更有效率(因为我们会跳过额外的计数查询):

# Answers were fetched via prefetch_related()
answer_count = len(question.answers.all())

所以我真正想做的是:

if question.answers_have_been_prefetched:  # Does this exist?
  answer_count = len(question.answers.all())
else:
  answer_count = question.answers.count()

如果重要的话,我正在使用 Django 1.4。提前致谢。

编辑:补充说明 prefetch_related() 不是获取答案的唯一方法。

【问题讨论】:

  • 这里使用包装库的全部意义在于,事实上,您不必担心这样的事情。除非您认为这是一个真正的瓶颈,否则只需使用简单的方法,不要增加不必要的复杂性。但是,在 Django 源代码中稍微戳了一下之后,我发现了一些提示。如果您仍然坚持尝试此 hack,您可以尝试obj._prefetched_objects_cache,或者您也可以尝试print dir(question.answers),看看您是否在其中看到任何与缓存相关的外观参数。
  • 这不是一个巨大的瓶颈,不。我只是想真正优化应用程序并将查询数量降至最低限度。同意如果唯一的方法是hacky,那么它是不值得的。我应该澄清一下:prefetch_related 只是一个例子。也可以通过其他方式获取结果(例如,迭代代码中之前的 question.answers),这就是为什么我希望有一些通用的解决方案。
  • 如果 Django 可以让你选择对预取关系进行自动内存过滤,那就太好了,这样你就不会仅仅通过使用他们的 DSL 来调查你已经拥有的数据来生成新的查询有。
  • 请注意,关系的“一”侧存储在 obj._{{field_name}}_cache 中
  • 自动内存过滤会很不错,尽管随着 Prefetch 对象的出现不太必要。

标签: python django


【解决方案1】:

是的,Django 将预取的结果存储在父模型实例的_prefetched_objects_cache 属性中。

所以你可以这样做:

instance = Parent.objects.prefetch_related('children').all()[0]

try:
    instance._prefetched_objects_cache[instance.children.prefetch_cache_name]
    # Ok, it's pefetched
    child_count = len(instance.children.all())
except (AttributeError, KeyError):
    # Not prefetched
    child_count = instance.children.count()

查看 django 源主干中的 relevant use 或 v1.4.9 中的 the equivalent

【讨论】:

  • 谢谢你,但我应该澄清一下:prefetch_related 只是一个例子。也可以通过其他方式获取结果(例如,迭代代码中之前的 question.answers)。
  • 相关的管理器访问通常不会被缓存,您可能必须实现自己的记忆包装器:docs.djangoproject.com/en/dev/topics/db/optimization/…
  • 啊,好的,很高兴知道。所以它们只有在你使用 prefetch_related 时才会被缓存。感谢您的澄清。听起来这个 hack 不值得付出努力,我应该只做 count(),但你给了我需要的信息。
  • Ok, it's prefetched 代码放入try … except … elseelse 块中可能是个好主意。
  • 预取的 GenericForeignKey 是否也存储在该变量中?
猜你喜欢
  • 2011-11-20
  • 1970-01-01
  • 1970-01-01
  • 2013-10-30
  • 1970-01-01
  • 2017-09-05
  • 1970-01-01
  • 2012-10-10
  • 2017-03-29
相关资源
最近更新 更多