代码之家  ›  专栏  ›  技术社区  ›  Klaas van Schelven

Django模型:在以下外键上保留对象标识

  •  2
  • Klaas van Schelven  · 技术社区  · 14 年前

    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和一个ReverseSingRelatedObjectSDescriptor。

    2 回复  |  直到 14 年前
        1
  •  6
  •   Daniel Roseman    14 年前

    对此有许多可能的解决方案。

    也许最简单的方法是自己跟踪父母:

    parent = Parents.objects.get(id=1)
    for child in parent.child_set.all():
        child._parent_cache = parent
    

    _FOO_cache django跟踪通过foreignkey获取的项的方式,因此如果您使用已经拥有的父对象预先填充子对象,django在引用时不会再次获取它 child.parent .

    或者,您可以查看试图修复此问题的第三方库之一。- django-idmapper django-selectreverse 我认识两个。

        2
  •  1
  •   Manoj Govindan    14 年前

    Django的ORM不遵循“反向”关系。这意味着每次访问 child.parent 它进行新的数据库调用。

    解决这一问题的一种方法是过滤 Child 对象与使用 select_related() 同时。这将减少子表和父表在查询执行时联接的次数或数据库调用,并且在 父母 被访问。

    例如

    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
    

    的python对象ID parent 父母 不会是相同的,但您将看到在访问时不会激发其他查询 父母 .