Как запоминать дорогостоящие вычисления на модельных объектах Django? - PullRequest
14 голосов
/ 06 октября 2009

У меня есть несколько столбцов TextField в моем объекте UserProfile, которые содержат объекты JSON. Я также определил свойство setter / getter для каждого столбца, который инкапсулирует логику для сериализации и десериализации JSON в структуры данных Python.

Характер этих данных гарантирует, что к ним будет многократно обращаться по логике представления и шаблона в рамках одного запроса. Чтобы сэкономить на затратах на десериализацию, я бы хотел запомнить структуры данных python при чтении, аннулировании при прямой записи в свойство или при сохранении сигнала от объекта модели.

Где / Как хранить заметку? Я нервничаю по поводу использования переменных экземпляра, так как я не понимаю магию, лежащую в основе того, как конкретный UserProfile создается запросом. Безопасно ли использовать __init__ или мне нужно проверять наличие атрибута memo с помощью hasattr() при каждом чтении?

Вот пример моей текущей реализации:

class UserProfile(Model):
    text_json = models.TextField(default=text_defaults)

    @property
    def text(self):
        if not hasattr(self, "text_memo"):
            self.text_memo = None
        self.text_memo = self.text_memo or simplejson.loads(self.text_json)
        return self.text_memo
    @text.setter
    def text(self, value=None):
        self.text_memo = None
        self.text_json = simplejson.dumps(value)

Ответы [ 3 ]

24 голосов
/ 14 апреля 2010

Вас может заинтересовать встроенный декоратор Django django.utils.functional.memoize.

Django использует это для кеширования дорогостоящей операции, такой как разрешение URL.

16 голосов
/ 06 октября 2009

Обычно я использую такой шаблон:

def get_expensive_operation(self):
    if not hasattr(self, '_expensive_operation'):
        self._expensive_operation = self.expensive_operation()
    return self._expensive_operation

Затем вы используете метод get_expensive_operation для доступа к данным.

Однако, в вашем конкретном случае, я думаю, вы подходите к этому немного неправильно. Вам необходимо выполнить десериализацию при первой загрузке модели из базы данных и сериализовать только при сохранении. Затем вы можете просто обращаться к атрибутам как к стандартному словарю Python каждый раз. Вы можете сделать это, определив пользовательский тип JSONField, создав подклассы models.TextField, который переопределяет to_python и get_db_prep_save.

На самом деле кто-то уже сделал это: см. здесь .

1 голос
/ 14 октября 2017

Для методов класса вы должны использовать django.utils.functional.cached_property.

Поскольку первый аргумент метода класса - self, memoize будет сохранять ссылку на объект и результаты функции даже после того, как вы его выбросили. Это может вызвать утечку памяти, не позволяя сборщику мусора очистить устаревший объект. cached_property превращает предложение Даниила в декоратора.

...