Skip to content

Commit

Permalink
do make cache invalidation from product model
Browse files Browse the repository at this point in the history
  • Loading branch information
jrief committed May 4, 2020
1 parent 10d852d commit dc77749
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 31 deletions.
45 changes: 20 additions & 25 deletions shop/admin/product.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import warnings

from django import forms
from django.core.cache import cache
from django.core.exceptions import ImproperlyConfigured
from django.contrib import admin
from django.contrib.sites.models import Site
from django.utils.translation import ugettext_lazy as _

try:
from django_elasticsearch_dsl.registries import registry as elasticsearch_registry
except ImportError:
elasticsearch_registry = type('DocumentRegistry', (), {'get_documents': lambda *args: []})()

from adminsortable2.admin import SortableInlineAdminMixin

from cms.models import Page
Expand Down Expand Up @@ -100,33 +102,26 @@ def save_related(self, request, form, formsets, change):
return super(CMSPageAsCategoryMixin, self).save_related(request, form, formsets, change)


class InvalidateProductCacheMixin(object):
class SearchProductIndexMixin:
"""
If caching is enabled, add this class as the first mixin to Django's model admin for the
corresponding product.
If Elasticsearch is used to create a full text search index, add this mixin class to Django's
``ModelAdmin`` backend for the corresponding product model.
"""
def __init__(self, *args, **kwargs):
if not hasattr(cache, 'delete_pattern'):
warnings.warn("\n"
"Your caching backend does not support deletion by key patterns.\n"
"Please use 'django-redis-cache', or wait until the product's HTML\n"
"snippet cache expires by itself.")
super(InvalidateProductCacheMixin, self).__init__(*args, **kwargs)
def save_model(self, request, product, form, change):
super().save_model(request, product, form, change)
if change:
product.update_search_index()


class InvalidateProductCacheMixin:
"""
If Redis caching is used to create a HTML snippets for product representation, add this mixin
class to Django's ``ModelAdmin`` backend for the corresponding product model.
"""
def save_model(self, request, product, form, change):
if change:
self.invalidate_cache(product)
super(InvalidateProductCacheMixin, self).save_model(request, product, form, change)

def invalidate_cache(self, product):
"""
The method ``ProductCommonSerializer.render_html`` caches the rendered HTML snippets.
Invalidate them after changing something in the product.
"""
try:
cache.delete_pattern('product:{}|*'.format(product.id))
except AttributeError:
pass
product.invalidate_cache()
return super().save_model(request, product, form, change)


class UnitPriceMixin(object):
Expand Down
14 changes: 13 additions & 1 deletion shop/apps.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import warnings

from django.apps import AppConfig
from django.core.cache import cache
from django.utils.translation import ugettext_lazy as _


class ShopConfig(AppConfig):
name = 'shop'
verbose_name = _("Shop")
cache_supporting_wildcard = False

def ready(self):
from shop.models.fields import JSONField
from rest_framework.serializers import ModelSerializer
from shop.deferred import ForeignKeyBuilder
from shop.models.fields import JSONField
from shop.rest.fields import JSONSerializerField
from shop.patches import PageAttribute
from cms.templatetags import cms_tags
Expand All @@ -21,3 +25,11 @@ def ready(self):
ForeignKeyBuilder.check_for_pending_mappings()

cms_tags.register.tags['page_attribute'] = PageAttribute

if callable(getattr(cache, 'delete_pattern', None)):
self.cache_supporting_wildcard = True
else:
warnings.warn("\n"
"Your caching backend does not support invalidation by key pattern.\n"
"Please use `django-redis-cache`, or wait until the product's HTML\n"
"snippet cache expires by itself.")
20 changes: 15 additions & 5 deletions shop/models/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
import operator
from cms import __version__ as CMS_VERSION

from django.apps import apps
from django.conf import settings
from django.core import checks
from django.core import cache, checks
from django.db import models
from django.db.models.aggregates import Sum
from django.db.models.functions import Coalesce
Expand Down Expand Up @@ -351,11 +352,11 @@ def check(cls, **kwargs):
errors.append(checks.Error(msg.format(cls.__name__)))
return errors

def save(self, *args, **kwargs):
super().save(*args, **kwargs)
self.update_search_index()

def update_search_index(self):
"""
Update the document model inside the Elasticsearch index after changing relevant parts
of the product.
"""
documents = elasticsearch_registry.get_documents([ProductModel])
if settings.USE_I18N:
for language, _ in settings.LANGUAGES:
Expand All @@ -368,6 +369,15 @@ def update_search_index(self):
document = next(doc for doc in documents)
document().update(self)

def invalidate_cache(self):
"""
Method ``ProductCommonSerializer.render_html()`` caches the rendered HTML snippets.
Invalidate this HTML snippet after changing relevant parts of the product.
"""
shop_app = apps.get_app_config('shop')
if shop_app.cache_supporting_wildcard:
cache.delete_pattern('product:{}|*'.format(self.id))

ProductModel = deferred.MaterializedModel(BaseProduct)


Expand Down

0 comments on commit dc77749

Please sign in to comment.