【问题标题】:Django Models: preserve object identity over foreign-key followingDjango模型:通过外键保留对象身份
【发布时间】:2010-09-15 11:50:11
【问题描述】:

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 => parent 链接访问的,因此在 parent 级别进行缓存是没有用的。

关于如何解决这个问题的任何想法?

我已经弄清楚有一个 ForeignRelatedObjectsDescriptor 和一个 ReverseSingleRelatedObjectDescriptor。

【问题讨论】:

    标签: django django-models


    【解决方案1】:

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

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

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

    _FOO_cache 是 Django 跟踪通过 ForeignKey 获取的项目的方式,因此如果您使用已有的父对象预先填充子对象上的该对象,当您引用 child.parent 时,Django 将不会再次获取它。

    或者,您可以查看尝试解决此问题的第三方库之一 - django-idmapperdjango-selectreverse 是我所知道的两个。

    【讨论】:

    • 谢谢。在我注意到您的回复之前,我确实实施了“自己跟踪父母”选项。不过,其他两种解决方案似乎也很有希望,所以我下次遇到这个问题时可能会研究它们。
    【解决方案2】:

    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 时不会触发其他查询。

    【讨论】:

    • 这样做的问题是您仍在向数据库添加不必要的工作——在这种情况下,是一个 JOIN——虽然不像整个单独的查询那样昂贵,但确实增加了一些权重。此外,OP 说他已经对 parent 对象进行了一些昂贵的计算 - 这些不会结转。
    • 你是对的。尤其是计算使得这不是一个有效的解决方案。
    • 正如丹尼尔所说:这并不能真正解决我的问题,因为“父母”中的计算成本很高。
    猜你喜欢
    • 2016-09-03
    • 1970-01-01
    • 2022-10-12
    • 2017-07-27
    • 2022-01-11
    • 1970-01-01
    • 2018-10-05
    • 2014-01-08
    • 2018-02-03
    相关资源
    最近更新 更多