Django的ORM(版本1.2.3)在来回跟踪外键时不保留标识。最好用一个例子来解释:
class Parent(models.Model):
pass
class Child(models.Model):
parent = models.ForeignKey(Parent)
parent = Parents.objects.get(id=1)
for child in parent.child_set.all():
print id(child.parent), "=!", id(parent)
因此,对于每个子节点,即使我们在获取子节点时知道父节点,也会从数据库中重新获取父节点。这对我来说是违反直觉的。
在我的情况下,这也会导致性能问题,因为我在父级别做了一些繁重的操作,我想在对象实例级别缓存。但是,由于这些计算的结果是通过child =>访问的。父链接,父级别的缓存是无用的。
关于如何解决这个问题的任何想法?
我已经知道有一个ForeignRelatedObjectsDescriptor和一个ReverseSingleRelatedObjectDescriptor。
答案 0 :(得分:6)
有很多可能的解决方案。
也许最简单的方法是自己跟踪父母:
parent = Parents.objects.get(id=1)
for child in parent.child_set.all():
child._parent_cache = parent
_FOO_cache
是Django跟踪通过ForeignKey获取的项目的方式,所以如果你用你已经拥有的父项预先填充该对象,Django将不会在你引用时再次获取它{{ 1}}。
或者,您可以查看尝试解决此问题的第三方库之一 - django-idmapper或django-selectreverse是我所知道的两个。
答案 1 :(得分:1)
Django的ORM不遵循“反向”关系。这意味着每次访问child.parent
时,都会进行新的数据库调用。
在某些(但不是全部)情况下解决此问题的一种方法是过滤Child
个对象并在执行此操作时使用select_related()
。这将减少数量或数据库调用,因为在查询执行时加入子表和父表,并且在访问child.parent
时不会触发单独的查询。
例如
from django.db import connection
parent = Parents.objects.get(id=1)
print parent
print len(connection.queries) # say, X
children = Child.objects.select_related().filter(parent = parent)
for child in children:
print child.parent
print len(connection.queries) # should be X + 1
parent
和child.parent
的Python对象ID不会相同,但您会看到在访问child.parent
时不会触发其他查询。