127 lines
3.7 KiB
Python
127 lines
3.7 KiB
Python
"""
|
|
Core methods
|
|
------------
|
|
|
|
.. autofunction:: cache_toolbox.core.get_instance
|
|
.. autofunction:: cache_toolbox.core.delete_instance
|
|
.. autofunction:: cache_toolbox.core.instance_key
|
|
|
|
"""
|
|
|
|
from django.core.cache import cache
|
|
from django.db import DEFAULT_DB_ALIAS
|
|
|
|
from . import app_settings
|
|
|
|
|
|
def get_instance(model, instance_or_pk, timeout=None, using=None):
|
|
"""
|
|
Returns the ``model`` instance with a primary key of ``instance_or_pk``.
|
|
|
|
If the data is cached it will be returned from there, otherwise the regular
|
|
Django ORM is queried for this instance and the data stored in the cache.
|
|
|
|
If omitted, the timeout value defaults to
|
|
``settings.CACHE_TOOLBOX_DEFAULT_TIMEOUT`` instead of 0 (zero).
|
|
|
|
Example::
|
|
|
|
>>> get_instance(User, 1) # Cache miss
|
|
<User: lamby>
|
|
>>> get_instance(User, 1) # Cache hit
|
|
<User: lamby>
|
|
>>> User.objects.get(pk=1) == get_instance(User, 1)
|
|
True
|
|
"""
|
|
|
|
pk = getattr(instance_or_pk, 'pk', instance_or_pk)
|
|
key = instance_key(model, instance_or_pk)
|
|
data = cache.get(key)
|
|
|
|
if data is not None:
|
|
try:
|
|
# Try and construct instance from dictionary
|
|
instance = model(pk=pk, **data)
|
|
|
|
# Ensure instance knows that it already exists in the database,
|
|
# otherwise we will fail any uniqueness checks when saving the
|
|
# instance.
|
|
instance._state.adding = False
|
|
|
|
# Specify database so that instance is setup correctly. We don't
|
|
# namespace cached objects by their origin database, however.
|
|
instance._state.db = using or DEFAULT_DB_ALIAS
|
|
|
|
return instance
|
|
except:
|
|
# Error when deserialising - remove from the cache; we will
|
|
# fallback and return the underlying instance
|
|
cache.delete(key)
|
|
|
|
# Use the default manager so we are never filtered by a .get_query_set()
|
|
|
|
# import logging
|
|
# log = logging.getLogger("tracking")
|
|
# log.info( str(pk) )
|
|
|
|
instance = model._default_manager.using(using).get(pk=pk)
|
|
|
|
data = {}
|
|
for field in instance._meta.fields:
|
|
# Harmless to save, but saves space in the dictionary - we already know
|
|
# the primary key when we lookup
|
|
if field.primary_key:
|
|
continue
|
|
|
|
if field.get_internal_type() == 'FileField':
|
|
# Avoid problems with serializing FileFields
|
|
# by only serializing the file name
|
|
file = getattr(instance, field.attname)
|
|
data[field.attname] = file.name
|
|
else:
|
|
data[field.attname] = getattr(instance, field.attname)
|
|
|
|
if timeout is None:
|
|
timeout = app_settings.CACHE_TOOLBOX_DEFAULT_TIMEOUT
|
|
|
|
cache.set(key, data, timeout)
|
|
|
|
return instance
|
|
|
|
|
|
def delete_instance(model, *instance_or_pk):
|
|
"""
|
|
Purges the cache keys for the instances of this model.
|
|
"""
|
|
|
|
cache.delete_many([instance_key(model, x) for x in instance_or_pk])
|
|
|
|
|
|
def instance_key(model, instance_or_pk):
|
|
"""
|
|
Returns the cache key for this (model, instance) pair.
|
|
"""
|
|
|
|
return '%s.%s:%d' % (
|
|
model._meta.app_label,
|
|
model._meta.module_name,
|
|
getattr(instance_or_pk, 'pk', instance_or_pk),
|
|
)
|
|
|
|
|
|
def set_cached_content(content):
|
|
cache.set(unicode(content.location).encode("utf-8"), content)
|
|
|
|
|
|
def get_cached_content(location):
|
|
return cache.get(unicode(location).encode("utf-8"))
|
|
|
|
|
|
def del_cached_content(location):
|
|
# delete content for the given location, as well as for content with run=None.
|
|
# it's possible that the content could have been cached without knowing the
|
|
# course_key - and so without having the run.
|
|
cache.delete_many(
|
|
[unicode(loc).encode("utf-8") for loc in [location, location.replace(run=None)]]
|
|
)
|