上下文
我在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)
Run Code Online (Sandbox Code Playgroud)
m = MyModel.objects.only('id').all()
Run Code Online (Sandbox Code Playgroud)
错误
该错误发生在以下行中(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))
Run Code Online (Sandbox Code Playgroud)
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)并从结果缓存中获取属性或数据源.它通过获取有问题的未延迟表示Model并only()在其上调用另一个查询来完成此操作.
这是循环遍历外键Model并访问其值时的问题,Cachine Machine引入了无意的递归.getattr(self, …
我有两个Django模型,如下所示,MyModel1&MyModel2:
class MyModel1(CachingMixin, MPTTModel):
name = models.CharField(null=False, blank=False, max_length=255)
objects = CachingManager()
def __str__(self):
return "; ".join(["ID: %s" % self.pk, "name: %s" % self.name, ] )
class MyModel2(CachingMixin, models.Model):
name = models.CharField(null=False, blank=False, max_length=255)
model1 = models.ManyToManyField(MyModel1, related_name="MyModel2_MyModel1")
objects = CachingManager()
def __str__(self):
return "; ".join(["ID: %s" % self.pk, "name: %s" % self.name, ] )
Run Code Online (Sandbox Code Playgroud)
MyModel2有一个ManyToMany字段来MyModel1授权model1
现在看看当我向这个ManyToMany字段添加一个新条目时会发生什么.据Django说,它没有效果:
>>> m1 = MyModel1.objects.all()[0]
>>> m2 = MyModel2.objects.all()[0]
>>> m2.model1.all()
[]
>>> m2.model1.add(m1)
>>> m2.model1.all() …Run Code Online (Sandbox Code Playgroud)