EVILEG-CORE. Caching properties of model objects using model_cached_property

ESNF-C, Django, model_cached_property, EVILEG

To speed up the site, in addition to optimizing database queries, you can use caching.

Django allows you to cache:

  • individual view , both Class Based View , and ordinary functions view
  • whole templates or parts of these templates
  • QuerySet
  • as well as properties of model objects using cached_property

I was interested in the ability to cache individual properties of model objects for heavy computing or heavy database queries.
The cached_property decorator has such a functional, but the drawback for me was that caching occurred only for the lifetime of the object.
Whereas I need caching for a longer period of time than the existence of an object when requesting a page. And also I needed to cache properties depending on the input arguments. This decorator on the site caches the number of likes and dislikes, as well as information about whether the current user liked a particular content object.

Thus the decorator model_cached_property was written

model_cached_property

This decorator uses redis as a caching backend, because invalidating the cache may require deleting the group of keys that belong to this property. Since the property can be cached for different users in different ways.

Intall EVILEG-CORE

pip install evileg-core

Also evileg_core will pull up all the dependencies necessary for this package. Including the django-redis library, which is used to work with redis .

If you are not using redis yet, you will need to install it.

sudo apt install redis-server

settings.py

Add evileg_core to installed applications

INSTALLED_APPS = [
    ...
    'evileg_core',
]

Configuring the caching backend

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}

Using model_cached_property

Normal caching

from evileg_core.cache.decorators import model_cached_property

class Article(models.Model):

    @model_cached_property
    def comments_count(self):
        return self.comments.count()

This decorator will cache the number of comments on the article for 60 seconds. At the next request for the article page, the request will be executed first to the cache for a specific article object, and only if the cache has expired, the request will again be executed to the database.

Set Caching Timeout

If you want to set a specific caching time, you can use the timeout argument with the decorator.

class Article(models.Model):

    @model_cached_property(timeout=3000)
    def comments_count(self):
        return self.comments.count()

Global setting of cache duration

You can also set the global caching time for all decorators in settings.py in seconds

MODEL_CACHED_PROPERTY_TIMEOUT = 300000

Using properties with arguments

And now for the fun part. Caching properties with arguments, thanks to which you can cache the result using information about the input arguments of the property of the data model.

This is both an advantage of this decorator and its disadvantage. The thing is that caching is correct, it is necessary that the input arguments are unique. For example, if the input arguments are temporary non-unique objects, like AnonymousUser , then caching will not work.

However, the decorator can be used for caching depending on the user. It might look like this.

class Article(models.Model):

    @model_cached_property
    def __user_in_bookmarks(self, user):
        return self.bookmarks.filter(user=user).exists()

    def user_in_bookmarks(self, user):
        return self.__user_in_bookmarks(user) if user.is_authenticated else False

Please note that there is a check for user.is_authenticated , because caching of an unauthenticated user will not work correctly, but it will work correctly for an authenticated user, since the authenticated user is a unique object.

Cache invalidation

In the event that the cache has become irrelevant before the expiration of its life, then you can use the function invalidate_model_cached_property

from evileg_core.cache.utils import invalidate_model_cached_property

class Article(models.Model):

    def invalidate_cache(self):
        invalidate_model_cached_property(self, self.comments_count)

For correct invalidation of the cache, you must pass the object whose cache you want to clear, as well as the method of this object.

It may look the same way.

article = get_object_or_404(Article, pk=12)
invalidate_model_cached_property(article, article.comments_count)

Conclusion

Thus model_cached_property can be used to

  • caching properties of model objects for a long time, more than the lifetime of the object when requesting a page
  • caching properties of model objects depending on input arguments

There are limitations

  • this decorator can only be used in models, that is, classes inherited from models.Model
  • caching of model properties depending on the arguments should be performed only for unique input arguments, otherwise caching will not be correct
We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.
- company blog
Support the author Donate
MK

а functools.lru_cache в данном случае не поможет?

Добрый день.

Думаю, что не совсем. Технически здесь решается задача кэширования не последних вызовов функции, как в lru_cache, а кэширование свойств для ряда объектов модели данных. К тому же cache_clear() будет полностью удалять весь кэш, тогда как у меня предусмотрен более специфический функционал для инвалидации части ключей, которые перестали быть актуальны.

Давайте поясню на примере.

На сайте есть модель данных ForumTopic , у которой есть лайки и дислайки, а также они подсвечиваются, если пользователь выбрал лайк или дислайк. Вся эта информация у меня кэшируется, поскольку для лайков и дислайков используются GenericForeignKey и запросы к базе данных посылаются отдельно, что получается несколько накладно для страниц со списком тем на форуме. Поэтому я предпочитаю кэшировать эти данные и при срабатывание некоторых событий делать инвалидацию кэша.

Так вот, если использовать lru_cache, то он будет кэшировать лишь n-последних вызовов метода, то есть придётся отслеживать количество контента и вручную поправлять количество последних кэширований, если решать эту задачу в лоб. А в случае очистки кэша, при вызове страниц будут заново кэшироваться все свойства для всех объектов ForumTopic. Получается ситуация, если пользователь лайкнул один единственный пост, то кэш будет очищаться для всех объектов, чего я не хочу.

model_cached_property же работает немного иначе. Он кэширует информацию о модели данных, а именно о таблице в базе данных, а также об id объекта, а потом информацию об имени свойства и входных аргументах. В результате происходит уникальное кжширование для каждого отдельного объекта.

Когда вы вызываете функцию invalidate_model_cached_property, то кэш очищается только для конкретного объекта базы данных.

Таким образом, когда пользователь лайкнет один какой-то объект ForumTopic , то кэш будет очищен только для этого ForumTopic . И при следующем запросе список объектов ForumTopic будет выполняться запрос о лайках и дислайках только для одного единственного объекта. Плюс я не беспокоюсь о том, что мне нужно думать, какой максимальный размер кэша нужно делать.

Конечно, вопрос расхода памяти на кэширование является довольно важным и если появится бутылочное горлышко, то я буду его решать. Но на данный момент я поставил у себя длительность кэширования подобных активностей, как лайки, на целый месяц и на данный момент ещё не заметил проблем.

Но если я что-то не учёл и у вас есть, что дополнить, то с удовольствием выслушаю вашу точку зрения, возможно, что это поможет доработать функциональность model_cached_property

Comments

Only authorized users can post comments.
Please, Log in or Sign up
Donate

Hello, Dear Users of EVILEG!!!

If the site helped you, then support the development of the site financially, please.

You can do it by following ways:

Thank you, Evgenii Legotckoi

Nov. 8, 2019, 7:59 a.m.
Pavel.K

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:60points,
  • Rating points-1
RF
Nov. 7, 2019, 12:51 p.m.
Roman Figura

C++ - Тест 003. Условия и циклы

  • Result:50points,
  • Rating points-4
RF
Nov. 7, 2019, 12:44 p.m.
Roman Figura

C++ - Test 002. Constants

  • Result:25points,
  • Rating points-10
Last comments
b
Nov. 9, 2019, 7:28 a.m.
bastonc

спасибо ещё раз. огромное, за уделённое время
b
Nov. 9, 2019, 7:24 a.m.
bastonc

Спасибо Вам большое. Буду изучать.
Nov. 9, 2019, 4:58 a.m.
Evgenij Legotskoj

Добрый день. По первым двум вопросам вы найдёте ответ в этой статье - PyQt5 - Урок 008. Работа с QTableWidget (Обновление урока 006) Что касается последнего вопроса, то я вам…
Nov. 9, 2019, 1:50 a.m.
Evgenij Legotskoj

Как и обещал, вы можете посмотреть новую статью QML - Урок 037. Кастомизация кнопок в QML (Обновление урока 002) . Там же найдёте ссылку на Git репозиторий. Не забудьте поставить звёз…
b
Nov. 8, 2019, 6:40 a.m.
bastonc

Приветствую. Подскажите пожалуйста пару моментов. 1. Как сделать столбец не редактируемый, а остальные ячейки остаются редактируемыми 2. Как оталвливать события двойного клика для реда…
Now discuss on the forum
AV
Nov. 11, 2019, 10:15 p.m.
Alexey Vasin

сейчас компа под рукой нет, так ты найдешь входит ли оди вектор в другой C++Выделит#include <algorithm>#include <iostream>#include <vector>using namespace std;int m…
r
Nov. 11, 2019, 4:57 a.m.
rbw123

buttonText скорее всего не видит потому, что он находится внутри ButtonStyle. А как тогда обращаться к свойствам?
Nov. 10, 2019, 5:53 a.m.
Evgenij Legotskoj

Я имел ввиду дополнительные параметры сортировки, кроме тех, что уже присутствуют в расширенном поиске.
c
Nov. 8, 2019, 10:06 a.m.
cappelikan

возникла задача реализовать парсинг html библиотекой htmlcxx и вывода href ссылок ввиде списка с помощью qlistview как это грамотно сделать ? спасибо
L
Nov. 7, 2019, 3:08 p.m.
LastLeaf

Спасибо, все получилось! Дай бог тебе здоровья!
EVILEG
About
Services
© EVILEG 2015-2019
Recommend hosting TIMEWEB