确定django中的属性是否为“DeferredAttribute”

时间:2015-01-29 18:58:38

标签: python django django-models django-orm django-cache-machine

上下文


我找到了Django Cache Machine中一个相当关键的错误,它导致它的失效逻辑在从Django 1.4升级到1.7后失去了理智。

在扩展缓存机器only()的模型上,该错误已本地化为CachingMixin的调用。它导致深度递归,偶尔会破坏堆栈,但否则会创建巨大的flush_lists缓存机器用于ForeignKey关系中的模型的双向失效。

class MyModel(CachingMixin):
    id = models.CharField(max_length=50, blank=True)
    nickname = models.CharField(max_length=50, blank=True)
    favorite_color = models.CharField(max_length=50, blank=True)
    content_owner = models.ForeignKey(OtherModel)

m = MyModel.objects.only('id').all()

错误


该错误发生在以下行(https://github.com/jbalogh/django-cache-machine/blob/f827f05b195ad3fc1b0111131669471d843d631f/caching/base.py#L253-L254)中。在这种情况下,selfMyModel的实例,其中包含延迟和未延迟属性:

    fks = dict((f, getattr(self, f.attname)) for f in self._meta.fields
                if isinstance(f, models.ForeignKey))

Cache Machine跨ForeignKey关系执行双向失效。它通过循环遍历Model中的所有字段并在缓存中存储一​​系列指针来执行此操作,这些指针指向在相关对象失效时需要无效的对象。

在Django ORM中使用only()做了一些元编程魔术,它通过Django的DeferredAttribute实现来覆盖未获取的属性。在正常情况下,访问favorite_color会调用DeferredAttribute.__get__https://github.com/django/django/blob/18f3e79b13947de0bda7c985916d5a04e28936dc/django/db/models/query_utils.py#L121-L146)并从结果缓存或数据源中获取属性。它通过获取有问题的Model的未延迟表示并在其上调用另一个only()查询来完成此操作。

当循环Model中的外键并访问它们的值时,这就是问题,Cachine Machine引入了无意的递归。对于延迟属性的getattr(self, f.attname)会导致获取已应用Model且具有延迟属性的CachingMixin。这将重新开始整个缓存过程。

问题


我想打开一个PR来解决这个问题,我相信这个问题的答案就像跳过延迟属性一样简单,但是我不知道怎么做,因为访问该属性会导致获取进程开始。

如果我所拥有的只是Model实例的句柄,并且混合了延迟和未延迟属性,是否有办法确定某个属性是DeferredAttribute 而不是< / strong>访问它?

    fks = dict((f, getattr(self, f.attname)) for f in self._meta.fields
                if (isinstance(f, models.ForeignKey) and <f's value isn't a Deferred attribute))

2 个答案:

答案 0 :(得分:8)

以下是检查某个字段是否延期的方法:

from django.db.models.query_utils import DeferredAttribute

is_deferred = isinstance(model_instance.__class__.__dict__.get(field.attname), DeferredAttribute):

取自:https://github.com/django/django/blob/1.9.4/django/db/models/base.py#L393

答案 1 :(得分:5)

这将检查属性是否是延迟属性并且尚未从数据库加载:

fks = dict((f, getattr(self, f.attname)) for f in self._meta.fields
                if (isinstance(f, models.ForeignKey) and f.attname in self.__dict__))

在内部,type(self)是原始类的newly created代理模型。 DeferredAttribute首先检查实例的local dict。如果不存在,它将从数据库加载值。此方法会绕过DeferredAttribute对象描述符,因此如果该值不存在,则不会加载该值。

这适用于Django 1.4和1.7,可能在两者之间的版本中。请注意,Django 1.8将在适当的时候引入get_deferred_fields()方法,该方法将取代所有这些干扰类内部的方法。