Commit 2c7975f8 by Nimisha Asthagiri

Move cache_toolbox from common to openedx/core.

parent 4bb88109
...@@ -347,7 +347,7 @@ MIDDLEWARE_CLASSES = ( ...@@ -347,7 +347,7 @@ MIDDLEWARE_CLASSES = (
'method_override.middleware.MethodOverrideMiddleware', 'method_override.middleware.MethodOverrideMiddleware',
# Instead of AuthenticationMiddleware, we use a cache-backed version # Instead of AuthenticationMiddleware, we use a cache-backed version
'cache_toolbox.middleware.CacheBackedAuthenticationMiddleware', 'openedx.core.djangoapps.cache_toolbox.middleware.CacheBackedAuthenticationMiddleware',
# Enable SessionAuthenticationMiddleware in order to invalidate # Enable SessionAuthenticationMiddleware in order to invalidate
# user sessions after a password change. # user sessions after a password change.
'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
......
...@@ -1098,7 +1098,7 @@ MIDDLEWARE_CLASSES = ( ...@@ -1098,7 +1098,7 @@ MIDDLEWARE_CLASSES = (
# Instead of AuthenticationMiddleware, we use a cached backed version # Instead of AuthenticationMiddleware, we use a cached backed version
#'django.contrib.auth.middleware.AuthenticationMiddleware', #'django.contrib.auth.middleware.AuthenticationMiddleware',
'cache_toolbox.middleware.CacheBackedAuthenticationMiddleware', 'openedx.core.djangoapps.cache_toolbox.middleware.CacheBackedAuthenticationMiddleware',
# Enable SessionAuthenticationMiddleware in order to invalidate # Enable SessionAuthenticationMiddleware in order to invalidate
# user sessions after a password change. # user sessions after a password change.
'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
......
"""
Settings for cache_toolbox.
"""
from django.conf import settings from django.conf import settings
# Default cache timeout # Default cache timeout
......
...@@ -34,40 +34,36 @@ def get_instance(model, instance_or_pk, timeout=None, using=None): ...@@ -34,40 +34,36 @@ def get_instance(model, instance_or_pk, timeout=None, using=None):
True True
""" """
pk = getattr(instance_or_pk, 'pk', instance_or_pk) primary_key = getattr(instance_or_pk, 'pk', instance_or_pk)
key = instance_key(model, instance_or_pk) key = instance_key(model, instance_or_pk)
data = cache.get(key) data = cache.get(key)
if data is not None: if data is not None:
try: try:
# Try and construct instance from dictionary # Try and construct instance from dictionary
instance = model(pk=pk, **data) instance = model(pk=primary_key, **data)
# Ensure instance knows that it already exists in the database, # Ensure instance knows that it already exists in the database,
# otherwise we will fail any uniqueness checks when saving the # otherwise we will fail any uniqueness checks when saving the
# instance. # instance.
instance._state.adding = False instance._state.adding = False # pylint: disable=protected-access
# Specify database so that instance is setup correctly. We don't # Specify database so that instance is setup correctly. We don't
# namespace cached objects by their origin database, however. # namespace cached objects by their origin database, however.
instance._state.db = using or DEFAULT_DB_ALIAS instance._state.db = using or DEFAULT_DB_ALIAS # pylint: disable=protected-access
return instance return instance
except: except: # pylint: disable=bare-except
# Error when deserialising - remove from the cache; we will # Error when deserialising - remove from the cache; we will
# fallback and return the underlying instance # fallback and return the underlying instance
cache.delete(key) cache.delete(key)
# Use the default manager so we are never filtered by a .get_queryset() # Use the default manager so we are never filtered by a .get_queryset()
# import logging instance = model._default_manager.using(using).get(pk=primary_key) # pylint: disable=protected-access
# log = logging.getLogger("tracking")
# log.info( str(pk) )
instance = model._default_manager.using(using).get(pk=pk)
data = {} data = {}
for field in instance._meta.fields: for field in instance._meta.fields: # pylint: disable=protected-access
# Harmless to save, but saves space in the dictionary - we already know # Harmless to save, but saves space in the dictionary - we already know
# the primary key when we lookup # the primary key when we lookup
if field.primary_key: if field.primary_key:
...@@ -76,8 +72,8 @@ def get_instance(model, instance_or_pk, timeout=None, using=None): ...@@ -76,8 +72,8 @@ def get_instance(model, instance_or_pk, timeout=None, using=None):
if field.get_internal_type() == 'FileField': if field.get_internal_type() == 'FileField':
# Avoid problems with serializing FileFields # Avoid problems with serializing FileFields
# by only serializing the file name # by only serializing the file name
file = getattr(instance, field.attname) file_value = getattr(instance, field.attname)
data[field.attname] = file.name data[field.attname] = file_value.name
else: else:
data[field.attname] = getattr(instance, field.attname) data[field.attname] = getattr(instance, field.attname)
......
...@@ -67,7 +67,7 @@ with:: ...@@ -67,7 +67,7 @@ with::
MIDDLEWARE_CLASSES = [ MIDDLEWARE_CLASSES = [
... ...
'cache_toolbox.middleware.CacheBackedAuthenticationMiddleware', 'openedx.core.djangoapps.cache_toolbox.middleware.CacheBackedAuthenticationMiddleware',
... ...
] ]
...@@ -93,6 +93,9 @@ log = getLogger(__name__) ...@@ -93,6 +93,9 @@ log = getLogger(__name__)
class CacheBackedAuthenticationMiddleware(AuthenticationMiddleware): class CacheBackedAuthenticationMiddleware(AuthenticationMiddleware):
"""
See documentation above.
"""
def __init__(self): def __init__(self):
cache_model(User) cache_model(User)
...@@ -110,7 +113,7 @@ class CacheBackedAuthenticationMiddleware(AuthenticationMiddleware): ...@@ -110,7 +113,7 @@ class CacheBackedAuthenticationMiddleware(AuthenticationMiddleware):
# Raise an exception to fall through to the except clause below. # Raise an exception to fall through to the except clause below.
raise Exception raise Exception
self._verify_session_auth(request) self._verify_session_auth(request)
except: except: # pylint: disable=bare-except
# Fallback to constructing the User from the database. # Fallback to constructing the User from the database.
super(CacheBackedAuthenticationMiddleware, self).process_request(request) super(CacheBackedAuthenticationMiddleware, self).process_request(request)
......
...@@ -60,18 +60,28 @@ from .core import get_instance, delete_instance ...@@ -60,18 +60,28 @@ from .core import get_instance, delete_instance
def cache_model(model, timeout=None): def cache_model(model, timeout=None):
"""
Adds utility methods to the given model to obtain
``ForeignKey`` instances via the cache.
"""
if hasattr(model, 'get_cached'): if hasattr(model, 'get_cached'):
# Already patched # Already patched
return return
def clear_cache(sender, instance, *args, **kwargs): def clear_cache(sender, instance, *args, **kwargs): # pylint: disable=unused-argument
"""
Clears the cache for the given instance.
"""
delete_instance(sender, instance) delete_instance(sender, instance)
post_save.connect(clear_cache, sender=model, weak=False) post_save.connect(clear_cache, sender=model, weak=False)
post_delete.connect(clear_cache, sender=model, weak=False) post_delete.connect(clear_cache, sender=model, weak=False)
@classmethod @classmethod
def get(cls, pk, using=None): def get(cls, pk, using=None): # pylint: disable=invalid-name
"""
Returns the model for the given primary key (pk).
"""
if pk is None: if pk is None:
return None return None
return get_instance(cls, pk, timeout, using) return get_instance(cls, pk, timeout, using)
......
...@@ -69,18 +69,25 @@ Support ...@@ -69,18 +69,25 @@ Support
``cache_relation`` currently only works with ``OneToOneField`` fields. Support ``cache_relation`` currently only works with ``OneToOneField`` fields. Support
for regular ``ForeignKey`` fields is planned. for regular ``ForeignKey`` fields is planned.
""" """
from django.db.models.signals import post_save, post_delete from django.db.models.signals import post_save, post_delete
from .core import get_instance, delete_instance from .core import get_instance, delete_instance
def cache_relation(descriptor, timeout=None): def cache_relation(descriptor, timeout=None):
"""
Adds utility methods to a model to obtain related
model instances via a cache.
"""
rel = descriptor.related rel = descriptor.related
related_name = '%s_cache' % rel.field.related_query_name() related_name = '%s_cache' % rel.field.related_query_name()
@property @property
def get(self): def get(self):
"""
Returns the cached value of the related model if found
in the cache. Otherwise gets and caches the related model.
"""
# Always use the cached "real" instance if available # Always use the cached "real" instance if available
try: try:
return getattr(self, descriptor.cache_name) return getattr(self, descriptor.cache_name)
...@@ -93,10 +100,6 @@ def cache_relation(descriptor, timeout=None): ...@@ -93,10 +100,6 @@ def cache_relation(descriptor, timeout=None):
except AttributeError: except AttributeError:
pass pass
# import logging
# log = logging.getLogger("tracking")
# log.info( "DEBUG: "+str(str(rel.model)+"/"+str(self.pk) ))
instance = get_instance(rel.model, self.pk, timeout) instance = get_instance(rel.model, self.pk, timeout)
setattr(self, '_%s_cache' % related_name, instance) setattr(self, '_%s_cache' % related_name, instance)
...@@ -107,13 +110,24 @@ def cache_relation(descriptor, timeout=None): ...@@ -107,13 +110,24 @@ def cache_relation(descriptor, timeout=None):
# Clearing cache # Clearing cache
def clear(self): def clear(self):
"""
Clears the cache of all related models of self.
"""
delete_instance(rel.model, self) delete_instance(rel.model, self)
@classmethod @classmethod
def clear_pk(cls, *instances_or_pk): def clear_pk(cls, *instances_or_pk): # pylint: disable=unused-argument
"""
Clears the cache of all related models of
the provided instances_or_pk.
"""
delete_instance(rel.model, *instances_or_pk) delete_instance(rel.model, *instances_or_pk)
def clear_cache(sender, instance, *args, **kwargs): def clear_cache(sender, instance, *args, **kwargs): # pylint: disable=unused-argument
"""
Clears the cache of all related models of the
given instance.
"""
delete_instance(rel.model, instance) delete_instance(rel.model, instance)
setattr(rel.parent_model, '%s_clear' % related_name, clear) setattr(rel.parent_model, '%s_clear' % related_name, clear)
......
"""
Implementation of custom django template tags for
automatically caching template fragments.
"""
from django import template from django import template
from django.core.cache import cache from django.core.cache import cache
from django.template import Node, TemplateSyntaxError, Variable from django.template import Node, TemplateSyntaxError, Variable
from django.template import resolve_variable from django.template import resolve_variable
register = template.Library() register = template.Library() # pylint: disable=invalid-name
class CacheNode(Node): class CacheNode(Node):
"""
Subclass of django's template Node class that
caches the rendered value of a template fragment. This is a
simpler implementation of django.templatetags.cache.CacheNode.
"""
def __init__(self, nodelist, expire_time, key): def __init__(self, nodelist, expire_time, key):
self.nodelist = nodelist self.nodelist = nodelist
self.expire_time = Variable(expire_time) self.expire_time = Variable(expire_time)
...@@ -46,6 +55,11 @@ def cachedeterministic(parser, token): ...@@ -46,6 +55,11 @@ def cachedeterministic(parser, token):
class ShowIfCachedNode(Node): class ShowIfCachedNode(Node):
"""
Subclass of django's template Node class that
renders the cached value for the given key, only
if already cached.
"""
def __init__(self, key): def __init__(self, key):
self.key = key self.key = key
...@@ -55,7 +69,7 @@ class ShowIfCachedNode(Node): ...@@ -55,7 +69,7 @@ class ShowIfCachedNode(Node):
@register.tag @register.tag
def showifcached(parser, token): def showifcached(parser, token): # pylint: disable=unused-argument
""" """
Show content if it exists in the cache, otherwise display nothing. Show content if it exists in the cache, otherwise display nothing.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment