在django中确定属性是否为“DeferredAttribute” [英] Determine if an attribute is a `DeferredAttribute` in django

查看:661
本文介绍了在django中确定属性是否为“DeferredAttribute”的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述






我在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆