Adds a signal to trigger post-invalidation behaviours.
This commit is contained in:
parent
389aded5bf
commit
3798a1c97d
|
@ -0,0 +1,7 @@
|
|||
# coding: utf-8
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from django.dispatch import Signal
|
||||
|
||||
|
||||
post_invalidation = Signal(providing_args=['db_alias'])
|
|
@ -5,3 +5,4 @@ from .thread_safety import ThreadSafetyTestCase
|
|||
from .multi_db import MultiDatabaseTestCase
|
||||
from .settings import SettingsTestCase
|
||||
from .api import APITestCase, CommandTestCase
|
||||
from .signals import SignalsTestCase
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
# coding: utf-8
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
try:
|
||||
from unittest import skipIf
|
||||
except ImportError: # For Python 2.6
|
||||
from unittest2 import skipIf
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import DEFAULT_DB_ALIAS
|
||||
from django.test import TransactionTestCase
|
||||
|
||||
from cachalot.signals import post_invalidation
|
||||
|
||||
from .models import Test
|
||||
|
||||
|
||||
class SignalsTestCase(TransactionTestCase):
|
||||
def test_table_invalidated(self):
|
||||
l = []
|
||||
|
||||
def receiver(sender, **kwargs):
|
||||
db_alias = kwargs['db_alias']
|
||||
l.append((sender, db_alias))
|
||||
|
||||
post_invalidation.connect(receiver)
|
||||
self.assertListEqual(l, [])
|
||||
list(Test.objects.all())
|
||||
self.assertListEqual(l, [])
|
||||
Test.objects.create(name='test1')
|
||||
self.assertListEqual(l, [('cachalot_test', DEFAULT_DB_ALIAS)])
|
||||
post_invalidation.disconnect(receiver)
|
||||
|
||||
del l[:] # Empties the list
|
||||
post_invalidation.connect(receiver, sender=User._meta.db_table)
|
||||
Test.objects.create(name='test2')
|
||||
self.assertListEqual(l, [])
|
||||
User.objects.create_user('user')
|
||||
self.assertListEqual(l, [('auth_user', DEFAULT_DB_ALIAS)])
|
||||
|
||||
@skipIf(len(settings.DATABASES) == 1,
|
||||
'We can’t change the DB used since there’s only one configured')
|
||||
def test_table_invalidated_multi_db(self):
|
||||
db_alias2 = next(alias for alias in settings.DATABASES
|
||||
if alias != DEFAULT_DB_ALIAS)
|
||||
l = []
|
||||
|
||||
def receiver(sender, **kwargs):
|
||||
db_alias = kwargs['db_alias']
|
||||
l.append((sender, db_alias))
|
||||
|
||||
post_invalidation.connect(receiver)
|
||||
self.assertListEqual(l, [])
|
||||
Test.objects.using(DEFAULT_DB_ALIAS).create(name='test')
|
||||
self.assertListEqual(l, [('cachalot_test', DEFAULT_DB_ALIAS)])
|
||||
Test.objects.using(db_alias2).create(name='test')
|
||||
self.assertListEqual(l, [
|
||||
('cachalot_test', DEFAULT_DB_ALIAS),
|
||||
('cachalot_test', db_alias2)])
|
||||
post_invalidation.disconnect(receiver)
|
|
@ -15,6 +15,7 @@ else:
|
|||
from django.utils.module_loading import import_by_path as import_string
|
||||
|
||||
from .settings import cachalot_settings
|
||||
from .signals import post_invalidation
|
||||
|
||||
|
||||
def get_query_cache_key(compiler):
|
||||
|
@ -99,16 +100,6 @@ def _get_tables(query, db_alias):
|
|||
|
||||
|
||||
def _get_table_cache_keys(compiler):
|
||||
"""
|
||||
Returns a ``list`` of cache keys for all the SQL tables used
|
||||
by ``compiler``.
|
||||
|
||||
:arg compiler: A SQLCompiler that will generate the SQL query
|
||||
:type compiler: django.db.models.sql.compiler.SQLCompiler
|
||||
:return: Cache keys for the SQL tables used
|
||||
:rtype: list
|
||||
"""
|
||||
|
||||
db_alias = compiler.using
|
||||
tables = _get_tables(compiler.query, db_alias)
|
||||
return [_get_table_cache_key(db_alias, t) for t in tables]
|
||||
|
@ -125,5 +116,10 @@ def _invalidate_table_cache_keys(cache, table_cache_keys):
|
|||
|
||||
|
||||
def _invalidate_tables(cache, compiler):
|
||||
table_cache_keys = _get_table_cache_keys(compiler)
|
||||
db_alias = compiler.using
|
||||
tables = _get_tables(compiler.query, db_alias)
|
||||
table_cache_keys = [_get_table_cache_key(db_alias, t) for t in tables]
|
||||
_invalidate_table_cache_keys(cache, table_cache_keys)
|
||||
|
||||
for table in tables:
|
||||
post_invalidation.send(table, db_alias=db_alias)
|
||||
|
|
|
@ -95,7 +95,6 @@ Dynamic overriding
|
|||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Django-cachalot is built so that its settings can be dynamically changed.
|
||||
|
||||
For example:
|
||||
|
||||
.. code:: python
|
||||
|
@ -112,3 +111,43 @@ For example:
|
|||
|
||||
# Globally disables SQL caching until you set it back to True
|
||||
settings.CACHALOT_ENABLED = False
|
||||
|
||||
|
||||
Signal
|
||||
......
|
||||
|
||||
``cachalot.signals.post_invalidation`` is available if you need to do something
|
||||
just after a cache invalidation (when you modify something in a SQL table).
|
||||
``sender`` is the name of the SQL table invalidated, and a keyword argument
|
||||
``db_alias`` explains which database is affected by the invalidation.
|
||||
Be careful when you specify ``sender``, as it is sensible to string type.
|
||||
To be sure, use ``Model._meta.db_table``.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
from cachalot.signals import post_invalidation
|
||||
from django.dispatch import receiver
|
||||
from django.core.mail import mail_admins
|
||||
from django.contrib.auth import *
|
||||
|
||||
# This prints a message to the console after each table invalidation
|
||||
def invalidation_debug(sender, **kwargs):
|
||||
db_alias = kwargs['db_alias']
|
||||
print('%s was invalidated in the DB configured as %s'
|
||||
% (sender, db_alias))
|
||||
|
||||
post_invalidation.connect(invalidation_debug)
|
||||
|
||||
# Using the `receiver` decorator is just a nicer way
|
||||
# to write the same thing as `signal.connect`.
|
||||
# Here we specify `sender` so that the function is executed only if
|
||||
# the table invalidated is the one specified.
|
||||
# We also connect it several times to be executed for several senders.
|
||||
@receiver(post_invalidation, sender=User.groups.through._meta.db_table)
|
||||
@receiver(post_invalidation, sender=User.user_permissions.through._meta.db_table)
|
||||
@receiver(post_invalidation, sender=Group.permissions.through._meta.db_table)
|
||||
def warn_admin(sender, **kwargs):
|
||||
mail_admins('User permissions changed',
|
||||
'Someone probably gained or lost Django permissions.')
|
||||
|
|
Loading…
Reference in New Issue