14

КонтекстОпределите, если атрибут является `DeferredAttribute` в Джанго


Я расположен довольно критическая ошибка в машине Джанго Cache, который вызывает это недействительности логика потерять свое мнение после обновления с Джанго 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). В этом случае self является экземпляром MyModel с соединением отсроченных и undeferred атрибутов:

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

Cache машина делает двунаправленные недействительности через ForeignKey отношений. Он делает это, перебирая все поля в 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, f.attname) на атрибут, который отложен, вызывает выборку Model, которая применяет CachingMixin и имеет отложенные атрибуты. Это снова запускает весь процесс кэширования.

Вопрос


Я хотел бы открыть PR, чтобы исправить это, и я считаю, что ответ на это так же просто, как пропуск через отсроченные атрибуты, но я не знаю, как сделайте это, потому что доступ к атрибуту заставляет процесс выборки запускаться.

Если все у меня есть ручка на экземпляре Model со смесью отсроченных и undeferred атрибутов, Есть ли способ определить, если атрибут является DeferredAttributeбез к нему доступ?

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)) 

ответ

6

Вот как проверить, если поле откладывается:

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

+0

Метод, использующий 'DeferredAttribute', кажется, не работает для меня на Django 1.7.3 ' instance .__ class __.__ dict__' никогда не имеет ключей от регулярных полей. –

+0

@CraynicCai, не уверен, почему он не работает для вас. Этот подход давно использовался Django. Здесь он находится в Django 1.7.3 https://github.com/django/django/blob/1.7.3/django/db/models/base.py#L402 –

4

Это будет проверять, если атрибут является отложенный атрибут и еще не загружены из базы данных:

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

Internally, type(self) - newly created Прокси-модель для оригинального класса. A DeferredAttribute сначала проверяет local dict экземпляра. Если этого не существует, он будет загружать значение из базы данных. Этот метод обходит дескриптор объекта DeferredAttribute, поэтому значение не будет загружено, если оно не существует.

Это работает в Django 1.4 и 1.7 и, предположительно, в версиях между ними. Обратите внимание, что Django 1.8 в свое время представит метод get_deferred_fields(), который заменит все это вмешательство со встроенными классами.

 Смежные вопросы

  • Нет связанных вопросов^_^