在django中确定属性是否为“DeferredAttribute” [英] Determine if an attribute is a `DeferredAttribute` in django
问题描述
我在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()
Bug / p>
错误发生在以下行( 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
关系。它通过循环在模型
中的所有字段,并将缓存中的一系列指针存储在指向当对象有问题的对象无效时的对象。 p>
在Django ORM中使用 only()
可以使用Django的 DeferredAttribute
实现。通常情况下,访问 favorite_color
将调用 DeferredAttribute .__ get __
( https://github.com/django/django/blob/18f3e79b13947de0bda7c985916d5a04e28936dc/django/db /models/query_utils.py#L121-L146 ),并从结果缓存或数据源中提取属性。它通过获取有问题的模型
的未延迟表示来执行此操作,并调用另一个查询。
当循环使用模型
中的外键并访问其值时,这是一个问题,Cachine Machine引入了无意的递归。对于延迟属性的 getattr(self,f.attname)
会导致获取模型
code> CachingMixin 应用并具有延迟属性。这将重新启动整个缓存过程。
问题
我想打开一个公关来解决这个问题,我相信这样做的答案就像跳过延迟属性一样简单,但是我不知道如何访问属性导致抓取过程开始。
如果我所有的都是一个模型
的实例的句柄,其中包含混合延迟和未延迟的属性,有没有办法确定属性是否为 DeferredAttribute
而不访问?
fks = dict((f,getattr(self,f.attname))for f in self._meta.fields
if(isinstance(f ,models.ForeignKey)和< f的值不是Deferred属性))
以下是如何检查字段是否被延期:
from django.db.models.query_utils导入DeferredAt tribute
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
The Context
I have located a rather critical bug in Django Cache Machine that causes it's invalidation logic to lose its mind after a upgrading from Django 1.4 to 1.7.
The bug is localized to invocations of only()
on models that extend cache machine's CachingMixin
. It results in deep recursions that occasionally bust the stack, but otherwise create huge flush_lists
that cache machine uses for bi-directional invalidation for models in ForeignKey
relationships.
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
The bug occurs in the following lines(https://github.com/jbalogh/django-cache-machine/blob/f827f05b195ad3fc1b0111131669471d843d631f/caching/base.py#L253-L254). In this case self
is a instance of MyModel
with a mix of deferred and undeferred attributes:
fks = dict((f, getattr(self, f.attname)) for f in self._meta.fields
if isinstance(f, models.ForeignKey))
Cache Machine does bidirectional invalidation across ForeignKey
relationships. It does this by looping over all the fields in a Model
and storing a series of pointers in cache that point to objects that need invalidated when the object in question is invalidated.
The use of only()
in the Django ORM does some meta programming magic that overrides the unfetched attributes with Django's DeferredAttribute
implementation. Under normal circumstances an access to favorite_color
would invoke DeferredAttribute.__get__
(https://github.com/django/django/blob/18f3e79b13947de0bda7c985916d5a04e28936dc/django/db/models/query_utils.py#L121-L146) and fetch the attribute either from the result cache or the data source. It does this by fetching the undeferred representation of the Model
in question and calling another only()
query on it.
This is the problem when looping over the foreign keys in the Model
and accessing their values, Cachine Machine introduces an unintentional recursion. getattr(self, f.attname)
on an attribute that is deferred induces a fetch of a Model
that has the CachingMixin
applied and has deferred attributes. This starts the whole caching process over again.
The Question
I would like to open a PR to fix this and I believe the answer to this is as simple as skipping over the deferred attributes, but I'm not sure how to do it because accessing the attribute causes the fetch process to start.
If all I have is a handle on an instance of a Model
with a mix of deferred and undeferred attributes, Is there a way to determine if an attribute is a DeferredAttribute
without accessing it?
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))
Here is how to check if a field is deferred:
from django.db.models.query_utils import DeferredAttribute
is_deferred = isinstance(model_instance.__class__.__dict__.get(field.attname), DeferredAttribute):
Taken from: https://github.com/django/django/blob/1.9.4/django/db/models/base.py#L393
这篇关于在django中确定属性是否为“DeferredAttribute”的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!