上下文
我在 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()
The Bug
该错误发生在以下几行中(https://github.com/jbalogh/django-cache-machine/blob/f827f05b195ad3fc1b0111131669471d843d631f/caching/base.py#L253-L254 https://github.com/jbalogh/django-cache-machine/blob/f827f05b195ad3fc1b0111131669471d843d631f/caching/base.py#L253-L254)。在这种情况下self
是一个实例MyModel
混合了延迟和非延迟属性:
fks = dict((f, getattr(self, f.attname)) for f in self._meta.fields
if isinstance(f, models.ForeignKey))
Cache Machine 进行双向失效ForeignKey
关系。它通过循环遍历 a 中的所有字段来做到这一点Model
当相关对象失效时,在缓存中存储一系列指向需要失效的对象的指针。
指某东西的用途only()
在 Django ORM 中,做了一些元编程魔法,用 Django 的覆盖未获取的属性DeferredAttribute
执行。正常情况下可以访问favorite_color
会调用DeferredAttribute.__get__
(https://github.com/django/django/blob/18f3e79b13947de0bda7c985916d5a04e28936dc/django/db/models/query_utils.py#L121-L146 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 来解决这个问题,我相信这个问题的答案就像跳过延迟属性一样简单,但我不确定如何做到这一点,因为访问该属性会导致获取过程启动。
如果我拥有的只是 a 实例的句柄Model
混合了延迟和非延迟属性,有没有办法确定一个属性是否是DeferredAttribute
without访问它?
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))