Django模型:通过外键跟随保留对象标识

时间:2010-09-15 11:50:11

标签: django django-models

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。

2 个答案:

答案 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-idmapperdjango-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

parentchild.parent的Python对象ID不会相同,但您会看到在访问child.parent时不会触发其他查询。