|PyPI Version| |Build Status|
django-adv-cache-tag
====================
Django advanced cache template tag:
- versioning
- compress
- partial caching
- easily extendable/customizable
Readable documentation on
http://documentup.com/twidi/django-adv-cache-tag
Introduction
------------
First, notice that the arguments of the ``{% cache %}`` templatetag
provided by ``django-adv-cache-tag`` are the same as for the default
cache templatetag included in django, so it's very easy to use this new
one.
With ``django-adv-cache-tag`` you can :
- add a version number (int, string, date or whatever, it will be
stringified) to your templatetag : the version will be compared to
the cached one, and the exact same cache key will be used for the new
cached template, avoiding keeping old unused keys in your cache,
allowing you to cache forever.
- avoid to be afraid of an incompatible update in our algorithm,
because we also use an internal version number, updated only when the
internal algorithm changes
- define your own cache keys (or simply, just add the primary key (or
what you want, it's a templatetag parameter) to this cache key
- compress the data to be cached, to reduce memory consumption in your
cache backend, and network latency (but it will use more time and cpu
to compress/decompress, your choice)
- choose which cache backend will be used
- define ``{% nocache %}...{% endnocache %}`` blocks inside your cached
template, that will only be rendered when asked (for these parts, the
content of the template is cached, not the rendered result)
- easily define your own algorithm, as we provide a single class (with
short methods) you can inherit from, and simply change options or
whatever behavior you want, and define your own tags for them
- use a variable for the name of your cache fragment
Installation
------------
``django-adv-cache-tag`` is available on PyPI::
pip install django-adv-cache-tag
Starting at version ``1.0``, we only support python 3.
**If you upgrade from version < 1, note that the internal version number has changed, so all
cache will be reset.**
If you want python 2 support, you must install by passing the version ::
pip install 'django-adv-cache-tag<1.0'
Or you can find it on Github:
https://github.com/twidi/django-adv-cache-tag
(for python2 version: https://github.com/twidi/django-adv-cache-tag/tree/python2)
When installed, just add ``adv_cache_tag`` to your ``INSTALLED_APPS`` in
the ``settings.py`` file of your django project.
See examples in the next sections to see how it works (basically the
same way as the default django cache templatetag)
Features
--------
Versioning
~~~~~~~~~~
Description
^^^^^^^^^^^
With the default django cache templatetag, you can add as many arguments
as you want, including a version, or date, and then the cache key will
change if this version change. So your cache is updated, as expected.
But the older key is not deleted and if you have a long expire time, it
will stay there for a very long time, consuming your precious memory.
``django-adv-cache-tag`` provides a way to avoid this, while still
regenerating the cache when needed. For this, when activated, we use the
last argument passed to your templatetag as a "version number", and
remove it for the arguments used to generate the cache key.
This version will be used in the **content** of the cached template,
instead of the **key**, and when the cache exists and is loaded, the
cached version will be compared to the wanted one, and if the two match,
the cache is valid and returned, else it will be regenerated.
So if you like the principle of a unique key for a given template for a
given object/user or whatever, be sure to always use the same arguments,
except the last one, and activate the ``ADV_CACHE_VERSIONING`` setting.
Note that we also manage an internal version number, which will always
be compared to the cached one. This internal version number is only
updated when the internal algorithm of ``django-adv-cache-tag`` changes.
But you can update it to invalidate all cached templates by adding a
``ADV_CACHE_VERSION`` to your settings (our internal version and the
value from this settings will be concatenated to get the internal
version really used)
Settings
^^^^^^^^
``ADV_CACHE_VERSIONING``, default to ``False``
``ADV_CACHE_VERSION``, default to ``""``
Example
^^^^^^^
In the following template, if ``ADV_CACHE_VERSIONING`` is set to True,
the key will always be the same, based on the string
"myobj\_main\_template" and the value of ``obj.pk``, but the cached
value will be regenerated each time the ``obj.date_last_updated`` will
change.
So we set a ``expire_time`` of ``0``, to always keep the template
cached, because we now we won't have many copies (old ones and current
one) of it.
The value to set to have no expiry may depend of your cache backend (it's not always ``0``).
.. code:: django
{% load adv_cache %}
{% cache 0 myobj_main_template obj.pk obj.date_last_updated %}
{{ obj }}
{% endcache %}
Primary key
~~~~~~~~~~~
Description
^^^^^^^^^^^
In the default django cache templatetag, the cache keys are like this
one ::
:1:template.cache.your_fragment_name.64223ccf70bbb65a3a4aceac37e21016
You may want to have more explicit cache keys, so with
``django-adv-cache-tag`` you can add a "primary key" that will be added
between the fragment name and the hash ::
:1:template.cache.your_fragment_name.your_pk.64223ccf70bbb65a3a4aceac37e21016
Although the main use of this primary key is to have one cached fragment
per object, so we can use the object primary key, you can use whatever
you want, an id, a string...
To add a primary key, simply set the ``ADV_CACHE_INCLUDE_PK`` setting to
``True``, and the first argument (after the fragment's name) will be
used as a pk.
If you want this only for a part of your cache templatetags, read the
``Extending the default cache tag`` part later in this readme (it's
easy, really).
Unlike the version, the primary key will be kept as an argument to
generate the cache key hash.
Settings
^^^^^^^^
``ADV_CACHE_INCLUDE_PK``, default to ``False``
Example
^^^^^^^
A common use of ``django-adv-cache-tag`` is to only use a primary key
and a version:
.. code:: django
{% cache 0 myobj_main_template obj.pk obj.date_last_updated %}
Compression
~~~~~~~~~~~
Description
^^^^^^^^^^^
The default django cache templatetag simply saves the generated html in
the cache. Depending of your template, if may be a lot of html and your
cache memory will grow very quickly. Not to mention that we can have a
lot of spaces because of indentation in templates (two ways i know to
remove them without ``django-adv-cache-tag``: the ``{% spaceless %}``
templatetag, provided by django, and
`django-template-preprocessor <https://github.com/citylive/django-template-preprocessor/>`__).
``django-adv-cache-tag`` can do this for you. It is able to remove
duplicate spaces (including newlines, tabs) by replacing them by a
simple space (to keep the space behavior in html), and to compress the
html to be cached, via the ``zlib`` (and ``pickle``) module.
Of course, this cost some time and CPU cycles, but you can save a lot of
memory in your cache backend, and a lot of bandwidth, especially if your
backend is on a distant place. I haven't done any test for this, but for
some templates, the saved data can be reduced from 2 ko to less than
one.
To activate these feature, simply set to ``True`` one or both of the
settings defined below.
WARNING : If the cache backend used use pickle and its default protocol,
compression is useless because binary is not really well handled and the
final size stored in the cache will be largely bigger than the
compressed one. So check for this before activating this option. It's ok
for the default django backends (at least in 1.4), but not for
django-redis-cache, waiting for my pull-request, but you can check my
own version:
https://github.com/twidi/django-redis-cache/tree/pickle\_version
Settings
^^^^^^^^
``ADV_CACHE_COMPRESS``, default to ``False``, to activate the
compression via ``zlib``
``ADV_CACHE_COMPRESS_LEVEL``, default to ``-1``, to set le compression level
for ``zlib`` (actually the default is ``zlib.Z_DEFAULT_COMPRESSION``, which is
``-1``, that will be in fact ``6`` as the actual default defined in ``zlib``)
``ADV_CACHE_COMPRESS_SPACES``, default to ``False``, to activate the
reduction of blank characters.
Example
^^^^^^^
No example since you don't have to change anything to your templatetag
call to use this, just set the settings.
Choose your cache backend
~~~~~~~~~~~~~~~~~~~~~~~~~
Description
^^^^^^^^^^^
In django, you can define many cache backends. But with the default
cache templatetag, you cannot say which one use, it will automatically
be the default one.
``django-adv-cache-tag`` can do this for your by providing a setting,
``ADV_CACHE_BACKEND`` which will take the name of a cache backend
defined in your settings. And by extending the provided ``CacheTag``
object, you can even define many backends to be used by many
templatetags, say one for heavily accessed templates, one for the
others... as you want. Read the ``Extending the default cache tag`` part
to know more about this (it's easy, really, but i already told you...)
Settings
^^^^^^^^
``ADV_CACHE_BACKEND``, default to "default"
Example
^^^^^^^
No example since, like for the compression, you don't have to change
anything to your templatetag to use this, just set the setting.
Partial caching
~~~~~~~~~~~~~~~
With the default django cache templatetag, your templates are cached and
you can't update them before display, so you can't cache big parts of
html with a little dynamic fragment in it, for the user name, the
current date or whatever. You can cheat and save two templates
surrounding your dynamic part, but you will have more accesses to your
cache backend.
``django-adv-cache-tag`` allow the use of one or many ``{% nocache %}``
blocks (closed by ``{% endnocache %}``) to put in your ``{% cache %}``
blocks. These ``{% nocache %}`` block will be saved "as is" in the
cache, while the rest of the block will be rendered to html. It's only
when the template is finally displayed that the no-cached parts will be
rendered.
You can have as many of these blocks you want.
Settings
^^^^^^^^
There is no settings for this feature, which is automatically activated.
Example
^^^^^^^
.. code:: django
{% cache 0 myobj_main_template obj.pk obj.date_last_updated %}
<p>This is the cached part of the template for {{ obj }}, evaluated at {% now "r" %}.</p>
{% nocache %}
<p>This part will be evaluated each time : {% now "r" %}</p>
{% endnocache %}
<p>This is another cached part</p>
{% endcache %}
The fragment name
~~~~~~~~~~~~~~~~~
Description
^^^^^^^^^^^
The fragment name is the name to use as a base to create the cache key, and is defined just
after the expiry time.
The Django documentation states ``The name will be taken as is, do not use a variable``.
In ``django-adv-cache-tag``, by setting ``ADV_CACHE_RESOLVE_NAME`` to ``True``, a fragment name
that is not quoted will be resolved as a variable that should be in the context.
Settings
^^^^^^^^
``ADV_CACHE_RESOLVE_NAME``, default to ``False``
Example
^^^^^^^
With ``ADV_CACHE_RESOLVE_NAME`` set to ``True``, you can do this if you have a variable named
``fragment_name`` in your context:
.. code:: django
{% cache 0 fragment_name obj.pk obj.date_last_updated %}
And if you want to pass a name, you have to surround it by quotes:
.. code:: django
{% cache 0 "myobj_main_template" obj.pk obj.date_last_updated %}
With ``ADV_CACHE_RESOLVE_NAME`` set to ``False``, the default, the name is always seen as a string,
but if surrounded by quotes, they are removed.
In the following example, you see double-quotes, but it would be the same with single quotes, or
no quotes at all:
.. code:: django
{% cache 0 "myobj_main_template" obj.pk obj.date_last_updated %}
Extending the default cache tag
-------------------------------
If the five settings explained in the previous sections are not enough
for you, or if you want to have a templatetag with a different behavior
as the default provided ones, you will be happy to know that
``django-adv-cache-tag`` was written with easily extending in mind.
It provides a class, ``CacheTag`` (in ``adv_cache_tag.tag``), which has
a lot of short and simple methods, and even a ``Meta`` class (idea
stolen from the django models :D ). So it's easy to override a simple
part.
All options defined in the ``Meta`` class are accessible in the class
via ``self.options.some_field``
Below we will show many ways of extending this class.
Basic override
~~~~~~~~~~~~~~
Imagine you don't want to change the default settings (all to ``False``,
and using the ``default`` backend) but want a templatetag with
versioning activated :
Create a new templatetag file (``myapp/templatetags/my_cache_tags.py``)
with this:
.. code:: python
from adv_cache_tag.tag import CacheTag
class VersionedCacheTag(CacheTag):
class Meta(CacheTag.Meta):
versioning = True
from django import template
register = template.Library()
VersionedCacheTag.register(register, 'ver_cache')
With these simple lines, you now have a new templatetag to use when you
want versioning:
.. code:: django
{% load my_cache_tags %}
{% ver_cache 0 myobj_main_template obj.pk obj.date_last_updated %}
obj
{% endver_cache %}
As you see, just replace ``{% load adv_cache %}`` (or the django default
``{% load cache %}``) by ``{% load my_cache_tags %}`` (your templatetag
module), and the ``{% cache %}`` templatetag by your new defined one,
``{% ver_cache ... %}``. Don't forget to replace the closing tag too:
``{% endver_cache %}``. But the ``{% nocache %}`` will stay the same,
except if you want a new one. For this, just add a parameter to the
``register`` method:
.. code:: python
MyCacheTag.register(register, 'ver_cache', 'ver_nocache')
.. code:: django
{% ver_cache ... %}
cached
{% ver_nocache %}not cached{% endver_nocache %}
{% endver_cache %}
Note that you can keep the name ``cache`` for your tag if you know that
you will not load in your template another templatetag module providing
a ``cache`` tag. To do so, the simplest way is:
.. code:: python
MyCacheTag.register(register) # 'cache' and 'nocache' are the default values
All the ``django-adv-cache-tag`` settings have a matching variable in
the ``Meta`` class, so you can override one or many of them in your own
classes. See the "Settings" part to see them.
Internal version
~~~~~~~~~~~~~~~~
When your template file is updated, the only way to invalidate all
cached versions of this template is to update the fragment name or the
arguments passed to the templatetag.
With ``django-adv-cache-tag`` you can do this with versioning, by
managing your own version as the last argument to the templatetag. But
if you want to use the power of the versioning system of
``django-adv-cache-tag``, it can be too verbose:
.. code:: django
{% load adv_cache %}
{% with template_version=obj.date_last_updated|stringformat:"s"|add:"v1" %}
{% cache 0 myobj_main_template obj.pk template_version %}
...
{% endcache %}
{% endwith %}
``django-adv-cache-tag`` provides a way to do this easily, with the
``ADV_CACHE_VERSION`` setting. But by updating it, **all** cached
versions will be invalidated, not only those you updated.
To do this, simply create your own tag with a specific internal version:
.. code:: python
class MyCacheTag(CacheTag):
class Meta(CacheTag.Meta):
internal_version = "v1"
MyCacheTag.register('my_cache')
And then in your template, you can simply do
.. code:: django
{% load my_cache_tags %}
{% my_cache 0 myobj_main_template obj.pk obj.date_last_updated %}
...
{% endmy_cache %}
Each time you update the content of your template and want invalidation,
simply change the ``internal_version`` in your ``MyCacheTag`` class (or
you can use a settings for this).
Change the cache backend
~~~~~~~~~~~~~~~~~~~~~~~~
If you want to change the cache backend for one templatetag, it's easy:
.. code:: python
class MyCacheTag(CacheTag):
class Meta:
cache_backend = 'templates'
But you can also to this by overriding a method:
.. code:: python
from django.core.cache import get_cache
class MyCacheTag(CacheTag):
def get_cache_object(self):
return get_cache('templates')
And if you want a cache backend for old objects, and another, faster,
for recent ones:
.. code:: python
from django.core.cache import get_cache
class MyCacheTag(CacheTag):
class Meta:
cache_backend = 'fast_templates'
def get_cache_object(self):
cache_backend = self.options.cache_backend
if self.get_pk() < 1000:
cache_backend = 'slow_templates'
return get_cache(cache_backend)
The value returned by the ``get_cache_object`` should be a cache backend
object, but as we only use the ``set`` and ``get`` methods on this
object, it can be what you want if it provides these two methods. And
even more, you can override the ``cache_set`` and ``cache_get`` methods
of the ``CacheTag`` class if you don't want to use the default ``set``
and ``get`` methods of the cache backend object.
Note that we also support the django way of changing the cache backend in the template-tag, using
the ``using`` argument, to be set at the last parameter (without any space between `using` and the
name of the cache backend).
.. code:: django
{% cache 0 myobj_main_template obj.pk obj.date_last_updated using=foo %}
Change the cache key
~~~~~~~~~~~~~~~~~~~~
The ``CacheTag`` class provides three classes to create the cache key:
- ``get_base_cache_key``, which returns a formatable string
("template.%(nodename)s.%(name)s.%(pk)s.%(hash)s" by default if
``include_pk`` is ``True`` or
"template.%(nodename)s.%(name)s.%(hash)s" if ``False``
- ``get_cache_key_args``, which returns the arguments to use in the
previous string
- ``get_cache_key``, which combine the two
The arguments are:
- ``nodename`` parameter is the name of the ``templatetag``: it's
"my\_cache" in ``{% my_cache ... %}``
- ``name`` is the "fragment name" of your templatetag, the value after
the expire-time
- ``pk`` is used only if ``self.options.include_pk`` is ``True``, and
is returned by ``this.get_pk()``
- ``hash`` is the hash of all arguments after the fragment name,
excluding the last one which is the version number (this exclusion
occurs only if ``self.options.versioning`` is ``True``)
If you want to remove the "template." part at the start of the cache key
(useless if you have a cache backend dedicated to template caching), you
can do this:
.. code:: python
class MyCacheTag(CacheTag):
def get_base_cache_key(self):
cache_key = super(MyCacheTag, self).get_base_cache_key()
return cache_key[len('template:'):] # or [9:]
Add an argument to the templatetag
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
By default, the templatetag provided by ``CacheTag`` takes the same
arguments as the default django cache templatetag.
If you want to add an argument, it's easy as the class provides a
``get_template_node_arguments`` method, which will work as for normal
django templatetags, taking a list of tokens, and returning ones that
will be passed to the real templatetag, a ``Node`` class tied to the
``CacheTag``.
Say you want to add a ``foo`` argument between the expire time and the
fragment name:
.. code:: python
from django import template
from adv_cache_tag.tag import CacheTag, Node
class MyNode(Node):
def __init__(self, nodename, nodelist, expire_time, foo, fragment_name, vary_on):
""" Save the foo variable in the node (not resolved yet) """
super(MyNode, self).__init__(self, nodename, nodelist, expire_time, fragment_name, vary_on)
self.foo = foo
class MyCacheTag(CacheTag):
Node = MyNode
def prepare_params(self):
""" Resolve the foo variable to it's real content """
super(MyCacheTag, self).prepare_params()
self.foo = template.Variable(self.node.foo).resolve(self.context)
@classmethod
def get_template_node_arguments(cls, tokens):
""" Check validity of tokens and return them as ready to be passed to the Node class """
if len(tokens) < 4:
raise template.TemplateSyntaxError(u"'%r' tag requires at least 3 arguments." % tokens[0])
return (tokens[1], tokens[2], tokens[3], tokens[4:])
Prepare caching of templates
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This one is not about overriding the class, but it can be useful. When
an object is updated, it can be better to regenerate the cached template
at this moment rather than when we need to display it.
It's easy. You can do this by catching the ``post_save`` signal of your
model, or just by overriding its ``save`` method. For this example we
will use this last solution.
The only special thing is to know the path of the template where your
templatetag is. In my case, i have a template just for this (included in
other ones for general use), so it's easier to find it and regenerate it
as in this example.
As we are not in a request, we don't have the ``Request`` object here,
so context processors are not working, we must create a context object
that will be used to render the template, with all needed variables.
.. code:: python
from django.template import loader, Context
class MyModel(models.Model):
# your fields
def save(self, *args, **kwargs):
super(MyModel, self.save(*args, **kwargs)
template = 'path/to/my_template_file_with_my_cache_block.html'
context = Context({
'obj': self,
# as you have no request, we have to add stuff from context processors manually if we need them
'STATIC_URL': settings.STATIC_URL,
# the line below indicates that we force regenerating the cache, even if it exists
'__regenerate__': True,
# the line below indicates if we only want html, without parsing the nocache parts
'__partial__': True,
})
loader.get_template(template).render(context)
Load data from database before rendering
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is a special case. Say you want to display a list of objects but
you have only ids and versions retrieved from redis (with ``ZSET``, with
id as value and updated date (which is used as a version) as score , for
example)
If you know you always have a valid version of your template in cache,
because they are regenerated every time they are saved, as seen above,
it's fine, just add the object's primary key as the ``pk`` in your
templatetag arguments, and the cached template will be loaded.
But if it's not the case, you will have a problem: when django will
render the template, the only part of the object present in the context
is the primary key, so if you need the name or whatever field to render
the cached template, it won't work.
With ``django-adv-cache-tag`` it's easy to resolve this, as we can load
the object from the database and adding it to the context.
View
^^^^
.. code:: python
def my_view(request):
objects = [
dict(
pk=val[0],
date_last_updated=val[1]
)
for val in
redis.zrevrange('my_objects', 0, 19, withscores=True)
]
return render(request, "my_results.html", dict(objects=objects))
Template "my\_results.html"
^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code:: django
{% for obj in objects %}
{% include "my_result.html" %}
{% endfor %}
Template "my\_result.html"
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code:: django
{% load my_cache_tags %}
{% my_cache 0 myobj_main_template obj.pk obj.date_last_updated %}
{{ obj }}
{% endmy_cache %}
Templatetag
^^^^^^^^^^^
In ``myapp/templatetags/my_cache_tags``
.. code:: python
from my_app.models import MyModel
class MyCacheTag(CacheTag):
class Meta(CacheTag.Meta):
""" Force options """
include_pk = True
versioning = True
def create_content(self):
""" If the object in context is not a real model, load it from db """
if not isinstance(context['obj'], MyObject):
context['obj'] = MyModel.objects.get(id=self.get_pk())
super(MyCacheTag, self).create_content()
MyCacheTag.register('my_cache')
Careful with this, it generates as database requests as objects to be
loaded.
And more...
~~~~~~~~~~~
If you want to do more, feel free to look at the source code of the
``CacheTag`` class (in ``tag.py``), all methods are documented.
Settings
--------
``django-adv-cache-tag`` provide 5 settings you can change. Here is the
list, with descriptions, default values, and corresponding fields in the
``Meta`` class (accessible via ``self.options.some_field`` in the
``CacheTag`` object)
- ``ADV_CACHE_VERSIONING`` to activate versioning, default to ``False``
(``versioning`` in the ``Meta`` class)
- ``ADV_CACHE_COMPRESS`` to activate compression, default to ``False``
(``compress`` in the ``Meta`` class)
- ``ADV_CACHE_COMPRESS_LEVEL`` to set the compression level (from ``1`` (min
compression) to ``9`` (max compression), default to ``-1`` (equivalent to
``6``) (``compress_level`` in the ``Meta`` class)
- ``ADV_CACHE_COMPRESS_SPACES`` to activate spaces compression, default
to ``False`` (``compress_spaces`` in the ``Meta`` class)
- ``ADV_CACHE_INCLUDE_PK`` to activate the "primary key" feature,
default to ``False`` (``include_pk`` in the ``Meta`` class)
- ``ADV_CACHE_BACKEND`` to choose the cache backend to use, default to
``"default"`` (``cache_backend`` in the ``Meta`` class)
- ``ADV_CACHE_VERSION`` to create your own internal version (will be
concatenated to the real internal version of
``django-adv-cache-tag``), default to ``""`` (``internal_version`` in
the ``Meta`` class)
How it works
------------
Here is a quick overview on how things work in ``django-adv-cache-tag``
Partial caching
~~~~~~~~~~~~~~~
Your template :
.. code:: django
{% load adv_cache %}
{% cache ... %}
foo
{% nocache %}
bar
{% endnocache %}
baz
{% endcache %}
Cached version (we ignore versioning and compress here, just to see how
it works):
.. code:: django
foo
{% endRAW_xyz %}
bar
{% RAW_xyz %}
baz
When cached version is loaded, we parse :
.. code:: django
{% RAW_xyz %}
foo
{% endRAW_xyz %}
bar
{% RAW_xyz %}
baz
{% endRAW_xyz %}
The first ``{% RAW_xyz %}`` and the last ``{% endRAW_xyz %}`` are not
included in the cached version and added before parsing, only to save
some bytes.
Parts between ``{% RAW_xyz %}`` and ``{% endRAW_xyz %}`` are not parsed
at all (seen as a ``TextNode`` by django)
The ``xyz`` part of the ``RAW`` and ``endRAW`` templatetags depends on
the ``SECRET_KEY`` and so is unique for a given site.
It allows to avoid at max the possible collisions with parsed content in
the cached version.
We could have used ``{% nocache %}`` and ``{% endnocache %}`` instead of
``{% RAW_xyz %}`` and ``{% endRAW_xyz %}`` but in the parsed template,
stored in the cache, if the html includes one of these strings, our
final template would be broken, so we use long ones with a hash (but we
can not be sure at 100% these strings could not be in the cached html,
but for common usages it should suffice)
License
-------
``django-adv-cache-tag`` is published under the MIT License (see the
LICENSE file)
Running tests
-------------
If ``adv_cache_tag`` is in the ``INSTALLED_APPS`` of your project, simply
run::
django-admin test adv_cache_tag
(you may want to use ``django-admin`` or ``./manage.py`` depending on
your installation)
If you are in a fresh virtualenv to work on ``adv_cache_tag``, install
the django version you want::
pip install django
Then make the ``adv_cache_tag`` module available in your python path.
For example, with ``virtualenv-wrapper``, considering you are at the
root of the ``django-adv-cache-tag`` repository, simply do::
add2virtualenv .
Or simply::
pip install -e .
Then to run the tests, this library provides a test project, so you can
launch them this way::
DJANGO_SETTINGS_MODULE=adv_cache_tag.tests.testproject.settings django-admin.py test adv_cache_tag
Or simply launch the ``runtests.sh`` script (it will run this exact
command)::
./runtests.sh
Supported versions
------------------
============== ============== ===============
Django version Python version Library version
============== ============== ===============
1.7 to 1.11 2.7 0.4
1.7 3.4 1.1.3
1.8 to 1.10 3.4, 3.5 1.1.3
1.11 3.4 to 3.6 1.1.3
2.0 3.4, to 3.7 1.1.3
2.1 3.5 to 3.7 1.1.3
2.2 3.5 to 3.8 1.1.3
3.0 3.6 to 3.8 1.1.3
============== ============== ===============
Support for Python 2 is dropped since version 1 of ``django-adv-cache-tag``
.. |PyPI Version| image:: https://img.shields.io/pypi/v/django-adv-cache-tag.png
:target: https://pypi.python.org/pypi/django-adv-cache-tag
:alt: PyPI Version
.. |Build Status| image:: https://travis-ci.org/twidi/django-adv-cache-tag.png
:target: https://travis-ci.org/twidi/django-adv-cache-tag
:alt: Build Status on Travis CI
Raw data
{
"_id": null,
"home_page": "https://github.com/erudit/django-adv-cache-tag",
"name": "erudit-django-adv-cache-tag",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.4",
"maintainer_email": "",
"keywords": "django,cache,templatetag,template",
"author": "Stephane \"Twidi\" Angel",
"author_email": "s.angel@twidi.com",
"download_url": "https://files.pythonhosted.org/packages/92/79/4dfa45193223cb0421793786061b0a80de8dba460097bbc3e46232fb9fdf/erudit-django-adv-cache-tag-1.1.5.tar.gz",
"platform": "any",
"description": "|PyPI Version| |Build Status|\n\ndjango-adv-cache-tag\n====================\n\nDjango advanced cache template tag:\n\n- versioning\n- compress\n- partial caching\n- easily extendable/customizable\n\nReadable documentation on\nhttp://documentup.com/twidi/django-adv-cache-tag\n\nIntroduction\n------------\n\nFirst, notice that the arguments of the ``{% cache %}`` templatetag\nprovided by ``django-adv-cache-tag`` are the same as for the default\ncache templatetag included in django, so it's very easy to use this new\none.\n\nWith ``django-adv-cache-tag`` you can :\n\n- add a version number (int, string, date or whatever, it will be\n stringified) to your templatetag : the version will be compared to\n the cached one, and the exact same cache key will be used for the new\n cached template, avoiding keeping old unused keys in your cache,\n allowing you to cache forever.\n- avoid to be afraid of an incompatible update in our algorithm,\n because we also use an internal version number, updated only when the\n internal algorithm changes\n- define your own cache keys (or simply, just add the primary key (or\n what you want, it's a templatetag parameter) to this cache key\n- compress the data to be cached, to reduce memory consumption in your\n cache backend, and network latency (but it will use more time and cpu\n to compress/decompress, your choice)\n- choose which cache backend will be used\n- define ``{% nocache %}...{% endnocache %}`` blocks inside your cached\n template, that will only be rendered when asked (for these parts, the\n content of the template is cached, not the rendered result)\n- easily define your own algorithm, as we provide a single class (with\n short methods) you can inherit from, and simply change options or\n whatever behavior you want, and define your own tags for them\n- use a variable for the name of your cache fragment\n\nInstallation\n------------\n\n``django-adv-cache-tag`` is available on PyPI::\n\n pip install django-adv-cache-tag\n\nStarting at version ``1.0``, we only support python 3.\n\n**If you upgrade from version < 1, note that the internal version number has changed, so all\ncache will be reset.**\n\nIf you want python 2 support, you must install by passing the version ::\n\n pip install 'django-adv-cache-tag<1.0'\n\nOr you can find it on Github:\nhttps://github.com/twidi/django-adv-cache-tag\n\n(for python2 version: https://github.com/twidi/django-adv-cache-tag/tree/python2)\n\nWhen installed, just add ``adv_cache_tag`` to your ``INSTALLED_APPS`` in\nthe ``settings.py`` file of your django project.\n\nSee examples in the next sections to see how it works (basically the\nsame way as the default django cache templatetag)\n\nFeatures\n--------\n\nVersioning\n~~~~~~~~~~\n\nDescription\n^^^^^^^^^^^\n\nWith the default django cache templatetag, you can add as many arguments\nas you want, including a version, or date, and then the cache key will\nchange if this version change. So your cache is updated, as expected.\n\nBut the older key is not deleted and if you have a long expire time, it\nwill stay there for a very long time, consuming your precious memory.\n\n``django-adv-cache-tag`` provides a way to avoid this, while still\nregenerating the cache when needed. For this, when activated, we use the\nlast argument passed to your templatetag as a \"version number\", and\nremove it for the arguments used to generate the cache key.\n\nThis version will be used in the **content** of the cached template,\ninstead of the **key**, and when the cache exists and is loaded, the\ncached version will be compared to the wanted one, and if the two match,\nthe cache is valid and returned, else it will be regenerated.\n\nSo if you like the principle of a unique key for a given template for a\ngiven object/user or whatever, be sure to always use the same arguments,\nexcept the last one, and activate the ``ADV_CACHE_VERSIONING`` setting.\n\nNote that we also manage an internal version number, which will always\nbe compared to the cached one. This internal version number is only\nupdated when the internal algorithm of ``django-adv-cache-tag`` changes.\nBut you can update it to invalidate all cached templates by adding a\n``ADV_CACHE_VERSION`` to your settings (our internal version and the\nvalue from this settings will be concatenated to get the internal\nversion really used)\n\nSettings\n^^^^^^^^\n\n``ADV_CACHE_VERSIONING``, default to ``False``\n\n``ADV_CACHE_VERSION``, default to ``\"\"``\n\nExample\n^^^^^^^\n\nIn the following template, if ``ADV_CACHE_VERSIONING`` is set to True,\nthe key will always be the same, based on the string\n\"myobj\\_main\\_template\" and the value of ``obj.pk``, but the cached\nvalue will be regenerated each time the ``obj.date_last_updated`` will\nchange.\n\nSo we set a ``expire_time`` of ``0``, to always keep the template\ncached, because we now we won't have many copies (old ones and current\none) of it.\n\nThe value to set to have no expiry may depend of your cache backend (it's not always ``0``).\n\n\n.. code:: django\n\n {% load adv_cache %}\n {% cache 0 myobj_main_template obj.pk obj.date_last_updated %}\n {{ obj }}\n {% endcache %}\n\nPrimary key\n~~~~~~~~~~~\n\nDescription\n^^^^^^^^^^^\n\nIn the default django cache templatetag, the cache keys are like this\none ::\n\n :1:template.cache.your_fragment_name.64223ccf70bbb65a3a4aceac37e21016\n\nYou may want to have more explicit cache keys, so with\n``django-adv-cache-tag`` you can add a \"primary key\" that will be added\nbetween the fragment name and the hash ::\n\n :1:template.cache.your_fragment_name.your_pk.64223ccf70bbb65a3a4aceac37e21016\n\nAlthough the main use of this primary key is to have one cached fragment\nper object, so we can use the object primary key, you can use whatever\nyou want, an id, a string...\n\nTo add a primary key, simply set the ``ADV_CACHE_INCLUDE_PK`` setting to\n``True``, and the first argument (after the fragment's name) will be\nused as a pk.\n\nIf you want this only for a part of your cache templatetags, read the\n``Extending the default cache tag`` part later in this readme (it's\neasy, really).\n\nUnlike the version, the primary key will be kept as an argument to\ngenerate the cache key hash.\n\nSettings\n^^^^^^^^\n\n``ADV_CACHE_INCLUDE_PK``, default to ``False``\n\nExample\n^^^^^^^\n\nA common use of ``django-adv-cache-tag`` is to only use a primary key\nand a version:\n\n.. code:: django\n\n {% cache 0 myobj_main_template obj.pk obj.date_last_updated %}\n\nCompression\n~~~~~~~~~~~\n\nDescription\n^^^^^^^^^^^\n\nThe default django cache templatetag simply saves the generated html in\nthe cache. Depending of your template, if may be a lot of html and your\ncache memory will grow very quickly. Not to mention that we can have a\nlot of spaces because of indentation in templates (two ways i know to\nremove them without ``django-adv-cache-tag``: the ``{% spaceless %}``\ntemplatetag, provided by django, and\n`django-template-preprocessor <https://github.com/citylive/django-template-preprocessor/>`__).\n\n``django-adv-cache-tag`` can do this for you. It is able to remove\nduplicate spaces (including newlines, tabs) by replacing them by a\nsimple space (to keep the space behavior in html), and to compress the\nhtml to be cached, via the ``zlib`` (and ``pickle``) module.\n\nOf course, this cost some time and CPU cycles, but you can save a lot of\nmemory in your cache backend, and a lot of bandwidth, especially if your\nbackend is on a distant place. I haven't done any test for this, but for\nsome templates, the saved data can be reduced from 2 ko to less than\none.\n\nTo activate these feature, simply set to ``True`` one or both of the\nsettings defined below.\n\nWARNING : If the cache backend used use pickle and its default protocol,\ncompression is useless because binary is not really well handled and the\nfinal size stored in the cache will be largely bigger than the\ncompressed one. So check for this before activating this option. It's ok\nfor the default django backends (at least in 1.4), but not for\ndjango-redis-cache, waiting for my pull-request, but you can check my\nown version:\nhttps://github.com/twidi/django-redis-cache/tree/pickle\\_version\n\nSettings\n^^^^^^^^\n\n``ADV_CACHE_COMPRESS``, default to ``False``, to activate the\ncompression via ``zlib``\n\n``ADV_CACHE_COMPRESS_LEVEL``, default to ``-1``, to set le compression level\nfor ``zlib`` (actually the default is ``zlib.Z_DEFAULT_COMPRESSION``, which is\n``-1``, that will be in fact ``6`` as the actual default defined in ``zlib``)\n\n``ADV_CACHE_COMPRESS_SPACES``, default to ``False``, to activate the\nreduction of blank characters.\n\nExample\n^^^^^^^\n\nNo example since you don't have to change anything to your templatetag\ncall to use this, just set the settings.\n\nChoose your cache backend\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nDescription\n^^^^^^^^^^^\n\nIn django, you can define many cache backends. But with the default\ncache templatetag, you cannot say which one use, it will automatically\nbe the default one.\n\n``django-adv-cache-tag`` can do this for your by providing a setting,\n``ADV_CACHE_BACKEND`` which will take the name of a cache backend\ndefined in your settings. And by extending the provided ``CacheTag``\nobject, you can even define many backends to be used by many\ntemplatetags, say one for heavily accessed templates, one for the\nothers... as you want. Read the ``Extending the default cache tag`` part\nto know more about this (it's easy, really, but i already told you...)\n\nSettings\n^^^^^^^^\n\n``ADV_CACHE_BACKEND``, default to \"default\"\n\nExample\n^^^^^^^\n\nNo example since, like for the compression, you don't have to change\nanything to your templatetag to use this, just set the setting.\n\nPartial caching\n~~~~~~~~~~~~~~~\n\nWith the default django cache templatetag, your templates are cached and\nyou can't update them before display, so you can't cache big parts of\nhtml with a little dynamic fragment in it, for the user name, the\ncurrent date or whatever. You can cheat and save two templates\nsurrounding your dynamic part, but you will have more accesses to your\ncache backend.\n\n``django-adv-cache-tag`` allow the use of one or many ``{% nocache %}``\nblocks (closed by ``{% endnocache %}``) to put in your ``{% cache %}``\nblocks. These ``{% nocache %}`` block will be saved \"as is\" in the\ncache, while the rest of the block will be rendered to html. It's only\nwhen the template is finally displayed that the no-cached parts will be\nrendered.\n\nYou can have as many of these blocks you want.\n\nSettings\n^^^^^^^^\n\nThere is no settings for this feature, which is automatically activated.\n\nExample\n^^^^^^^\n\n.. code:: django\n\n {% cache 0 myobj_main_template obj.pk obj.date_last_updated %}\n <p>This is the cached part of the template for {{ obj }}, evaluated at {% now \"r\" %}.</p>\n {% nocache %}\n <p>This part will be evaluated each time : {% now \"r\" %}</p>\n {% endnocache %}\n <p>This is another cached part</p>\n {% endcache %}\n\nThe fragment name\n~~~~~~~~~~~~~~~~~\n\nDescription\n^^^^^^^^^^^\n\nThe fragment name is the name to use as a base to create the cache key, and is defined just\nafter the expiry time.\n\nThe Django documentation states ``The name will be taken as is, do not use a variable``.\n\nIn ``django-adv-cache-tag``, by setting ``ADV_CACHE_RESOLVE_NAME`` to ``True``, a fragment name\nthat is not quoted will be resolved as a variable that should be in the context.\n\nSettings\n^^^^^^^^\n\n``ADV_CACHE_RESOLVE_NAME``, default to ``False``\n\nExample\n^^^^^^^\n\nWith ``ADV_CACHE_RESOLVE_NAME`` set to ``True``, you can do this if you have a variable named\n``fragment_name`` in your context:\n\n.. code:: django\n\n {% cache 0 fragment_name obj.pk obj.date_last_updated %}\n\nAnd if you want to pass a name, you have to surround it by quotes:\n\n.. code:: django\n\n {% cache 0 \"myobj_main_template\" obj.pk obj.date_last_updated %}\n\nWith ``ADV_CACHE_RESOLVE_NAME`` set to ``False``, the default, the name is always seen as a string,\nbut if surrounded by quotes, they are removed.\n\nIn the following example, you see double-quotes, but it would be the same with single quotes, or\nno quotes at all:\n\n.. code:: django\n\n {% cache 0 \"myobj_main_template\" obj.pk obj.date_last_updated %}\n\nExtending the default cache tag\n-------------------------------\n\nIf the five settings explained in the previous sections are not enough\nfor you, or if you want to have a templatetag with a different behavior\nas the default provided ones, you will be happy to know that\n``django-adv-cache-tag`` was written with easily extending in mind.\n\nIt provides a class, ``CacheTag`` (in ``adv_cache_tag.tag``), which has\na lot of short and simple methods, and even a ``Meta`` class (idea\nstolen from the django models :D ). So it's easy to override a simple\npart.\n\nAll options defined in the ``Meta`` class are accessible in the class\nvia ``self.options.some_field``\n\nBelow we will show many ways of extending this class.\n\nBasic override\n~~~~~~~~~~~~~~\n\nImagine you don't want to change the default settings (all to ``False``,\nand using the ``default`` backend) but want a templatetag with\nversioning activated :\n\nCreate a new templatetag file (``myapp/templatetags/my_cache_tags.py``)\nwith this:\n\n.. code:: python\n\n from adv_cache_tag.tag import CacheTag\n\n class VersionedCacheTag(CacheTag):\n class Meta(CacheTag.Meta):\n versioning = True\n\n from django import template\n register = template.Library()\n\n VersionedCacheTag.register(register, 'ver_cache')\n\nWith these simple lines, you now have a new templatetag to use when you\nwant versioning:\n\n.. code:: django\n\n {% load my_cache_tags %}\n {% ver_cache 0 myobj_main_template obj.pk obj.date_last_updated %}\n obj\n {% endver_cache %}\n\nAs you see, just replace ``{% load adv_cache %}`` (or the django default\n``{% load cache %}``) by ``{% load my_cache_tags %}`` (your templatetag\nmodule), and the ``{% cache %}`` templatetag by your new defined one,\n``{% ver_cache ... %}``. Don't forget to replace the closing tag too:\n``{% endver_cache %}``. But the ``{% nocache %}`` will stay the same,\nexcept if you want a new one. For this, just add a parameter to the\n``register`` method:\n\n.. code:: python\n\n MyCacheTag.register(register, 'ver_cache', 'ver_nocache')\n\n.. code:: django\n\n {% ver_cache ... %}\n cached\n {% ver_nocache %}not cached{% endver_nocache %}\n {% endver_cache %}\n\nNote that you can keep the name ``cache`` for your tag if you know that\nyou will not load in your template another templatetag module providing\na ``cache`` tag. To do so, the simplest way is:\n\n.. code:: python\n\n MyCacheTag.register(register) # 'cache' and 'nocache' are the default values\n\nAll the ``django-adv-cache-tag`` settings have a matching variable in\nthe ``Meta`` class, so you can override one or many of them in your own\nclasses. See the \"Settings\" part to see them.\n\nInternal version\n~~~~~~~~~~~~~~~~\n\nWhen your template file is updated, the only way to invalidate all\ncached versions of this template is to update the fragment name or the\narguments passed to the templatetag.\n\nWith ``django-adv-cache-tag`` you can do this with versioning, by\nmanaging your own version as the last argument to the templatetag. But\nif you want to use the power of the versioning system of\n``django-adv-cache-tag``, it can be too verbose:\n\n.. code:: django\n\n {% load adv_cache %}\n {% with template_version=obj.date_last_updated|stringformat:\"s\"|add:\"v1\" %}\n {% cache 0 myobj_main_template obj.pk template_version %}\n ...\n {% endcache %}\n {% endwith %}\n\n``django-adv-cache-tag`` provides a way to do this easily, with the\n``ADV_CACHE_VERSION`` setting. But by updating it, **all** cached\nversions will be invalidated, not only those you updated.\n\nTo do this, simply create your own tag with a specific internal version:\n\n.. code:: python\n\n class MyCacheTag(CacheTag):\n class Meta(CacheTag.Meta):\n internal_version = \"v1\"\n\n MyCacheTag.register('my_cache')\n\nAnd then in your template, you can simply do\n\n.. code:: django\n\n {% load my_cache_tags %}\n {% my_cache 0 myobj_main_template obj.pk obj.date_last_updated %}\n ...\n {% endmy_cache %}\n\nEach time you update the content of your template and want invalidation,\nsimply change the ``internal_version`` in your ``MyCacheTag`` class (or\nyou can use a settings for this).\n\nChange the cache backend\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you want to change the cache backend for one templatetag, it's easy:\n\n.. code:: python\n\n class MyCacheTag(CacheTag):\n class Meta:\n cache_backend = 'templates'\n\nBut you can also to this by overriding a method:\n\n.. code:: python\n\n from django.core.cache import get_cache\n\n class MyCacheTag(CacheTag):\n def get_cache_object(self):\n return get_cache('templates')\n\nAnd if you want a cache backend for old objects, and another, faster,\nfor recent ones:\n\n.. code:: python\n\n from django.core.cache import get_cache\n\n class MyCacheTag(CacheTag):\n class Meta:\n cache_backend = 'fast_templates'\n\n def get_cache_object(self):\n cache_backend = self.options.cache_backend\n if self.get_pk() < 1000:\n cache_backend = 'slow_templates'\n return get_cache(cache_backend)\n\nThe value returned by the ``get_cache_object`` should be a cache backend\nobject, but as we only use the ``set`` and ``get`` methods on this\nobject, it can be what you want if it provides these two methods. And\neven more, you can override the ``cache_set`` and ``cache_get`` methods\nof the ``CacheTag`` class if you don't want to use the default ``set``\nand ``get`` methods of the cache backend object.\n\nNote that we also support the django way of changing the cache backend in the template-tag, using\nthe ``using`` argument, to be set at the last parameter (without any space between `using` and the\nname of the cache backend).\n\n.. code:: django\n\n {% cache 0 myobj_main_template obj.pk obj.date_last_updated using=foo %}\n\n\nChange the cache key\n~~~~~~~~~~~~~~~~~~~~\n\nThe ``CacheTag`` class provides three classes to create the cache key:\n\n- ``get_base_cache_key``, which returns a formatable string\n (\"template.%(nodename)s.%(name)s.%(pk)s.%(hash)s\" by default if\n ``include_pk`` is ``True`` or\n \"template.%(nodename)s.%(name)s.%(hash)s\" if ``False``\n- ``get_cache_key_args``, which returns the arguments to use in the\n previous string\n- ``get_cache_key``, which combine the two\n\nThe arguments are:\n\n- ``nodename`` parameter is the name of the ``templatetag``: it's\n \"my\\_cache\" in ``{% my_cache ... %}``\n- ``name`` is the \"fragment name\" of your templatetag, the value after\n the expire-time\n- ``pk`` is used only if ``self.options.include_pk`` is ``True``, and\n is returned by ``this.get_pk()``\n- ``hash`` is the hash of all arguments after the fragment name,\n excluding the last one which is the version number (this exclusion\n occurs only if ``self.options.versioning`` is ``True``)\n\nIf you want to remove the \"template.\" part at the start of the cache key\n(useless if you have a cache backend dedicated to template caching), you\ncan do this:\n\n.. code:: python\n\n class MyCacheTag(CacheTag):\n def get_base_cache_key(self):\n cache_key = super(MyCacheTag, self).get_base_cache_key()\n return cache_key[len('template:'):] # or [9:]\n\nAdd an argument to the templatetag\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nBy default, the templatetag provided by ``CacheTag`` takes the same\narguments as the default django cache templatetag.\n\nIf you want to add an argument, it's easy as the class provides a\n``get_template_node_arguments`` method, which will work as for normal\ndjango templatetags, taking a list of tokens, and returning ones that\nwill be passed to the real templatetag, a ``Node`` class tied to the\n``CacheTag``.\n\nSay you want to add a ``foo`` argument between the expire time and the\nfragment name:\n\n.. code:: python\n\n from django import template\n\n from adv_cache_tag.tag import CacheTag, Node\n\n class MyNode(Node):\n def __init__(self, nodename, nodelist, expire_time, foo, fragment_name, vary_on):\n \"\"\" Save the foo variable in the node (not resolved yet) \"\"\"\n super(MyNode, self).__init__(self, nodename, nodelist, expire_time, fragment_name, vary_on)\n self.foo = foo\n\n\n class MyCacheTag(CacheTag):\n\n Node = MyNode\n\n def prepare_params(self):\n \"\"\" Resolve the foo variable to it's real content \"\"\"\n super(MyCacheTag, self).prepare_params()\n self.foo = template.Variable(self.node.foo).resolve(self.context)\n\n @classmethod\n def get_template_node_arguments(cls, tokens):\n \"\"\" Check validity of tokens and return them as ready to be passed to the Node class \"\"\"\n if len(tokens) < 4:\n raise template.TemplateSyntaxError(u\"'%r' tag requires at least 3 arguments.\" % tokens[0])\n return (tokens[1], tokens[2], tokens[3], tokens[4:])\n\nPrepare caching of templates\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis one is not about overriding the class, but it can be useful. When\nan object is updated, it can be better to regenerate the cached template\nat this moment rather than when we need to display it.\n\nIt's easy. You can do this by catching the ``post_save`` signal of your\nmodel, or just by overriding its ``save`` method. For this example we\nwill use this last solution.\n\nThe only special thing is to know the path of the template where your\ntemplatetag is. In my case, i have a template just for this (included in\nother ones for general use), so it's easier to find it and regenerate it\nas in this example.\n\nAs we are not in a request, we don't have the ``Request`` object here,\nso context processors are not working, we must create a context object\nthat will be used to render the template, with all needed variables.\n\n.. code:: python\n\n from django.template import loader, Context\n\n class MyModel(models.Model):\n # your fields\n\n def save(self, *args, **kwargs):\n super(MyModel, self.save(*args, **kwargs)\n\n template = 'path/to/my_template_file_with_my_cache_block.html'\n\n context = Context({\n 'obj': self,\n\n # as you have no request, we have to add stuff from context processors manually if we need them\n 'STATIC_URL': settings.STATIC_URL,\n\n # the line below indicates that we force regenerating the cache, even if it exists\n '__regenerate__': True,\n\n # the line below indicates if we only want html, without parsing the nocache parts\n '__partial__': True,\n\n })\n\n loader.get_template(template).render(context)\n\nLoad data from database before rendering\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis is a special case. Say you want to display a list of objects but\nyou have only ids and versions retrieved from redis (with ``ZSET``, with\nid as value and updated date (which is used as a version) as score , for\nexample)\n\nIf you know you always have a valid version of your template in cache,\nbecause they are regenerated every time they are saved, as seen above,\nit's fine, just add the object's primary key as the ``pk`` in your\ntemplatetag arguments, and the cached template will be loaded.\n\nBut if it's not the case, you will have a problem: when django will\nrender the template, the only part of the object present in the context\nis the primary key, so if you need the name or whatever field to render\nthe cached template, it won't work.\n\nWith ``django-adv-cache-tag`` it's easy to resolve this, as we can load\nthe object from the database and adding it to the context.\n\nView\n^^^^\n\n.. code:: python\n\n def my_view(request):\n objects = [\n dict(\n pk=val[0],\n date_last_updated=val[1]\n )\n for val in\n redis.zrevrange('my_objects', 0, 19, withscores=True)\n ]\n return render(request, \"my_results.html\", dict(objects=objects))\n\nTemplate \"my\\_results.html\"\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code:: django\n\n {% for obj in objects %}\n {% include \"my_result.html\" %}\n {% endfor %}\n\nTemplate \"my\\_result.html\"\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code:: django\n\n {% load my_cache_tags %}\n {% my_cache 0 myobj_main_template obj.pk obj.date_last_updated %}\n {{ obj }}\n {% endmy_cache %}\n\nTemplatetag\n^^^^^^^^^^^\n\nIn ``myapp/templatetags/my_cache_tags``\n\n.. code:: python\n\n from my_app.models import MyModel\n\n class MyCacheTag(CacheTag):\n\n class Meta(CacheTag.Meta):\n \"\"\" Force options \"\"\"\n include_pk = True\n versioning = True\n\n def create_content(self):\n \"\"\" If the object in context is not a real model, load it from db \"\"\"\n if not isinstance(context['obj'], MyObject):\n context['obj'] = MyModel.objects.get(id=self.get_pk())\n super(MyCacheTag, self).create_content()\n\n MyCacheTag.register('my_cache')\n\nCareful with this, it generates as database requests as objects to be\nloaded.\n\nAnd more...\n~~~~~~~~~~~\n\nIf you want to do more, feel free to look at the source code of the\n``CacheTag`` class (in ``tag.py``), all methods are documented.\n\nSettings\n--------\n\n``django-adv-cache-tag`` provide 5 settings you can change. Here is the\nlist, with descriptions, default values, and corresponding fields in the\n``Meta`` class (accessible via ``self.options.some_field`` in the\n``CacheTag`` object)\n\n- ``ADV_CACHE_VERSIONING`` to activate versioning, default to ``False``\n (``versioning`` in the ``Meta`` class)\n- ``ADV_CACHE_COMPRESS`` to activate compression, default to ``False``\n (``compress`` in the ``Meta`` class)\n- ``ADV_CACHE_COMPRESS_LEVEL`` to set the compression level (from ``1`` (min\n compression) to ``9`` (max compression), default to ``-1`` (equivalent to\n ``6``) (``compress_level`` in the ``Meta`` class)\n- ``ADV_CACHE_COMPRESS_SPACES`` to activate spaces compression, default\n to ``False`` (``compress_spaces`` in the ``Meta`` class)\n- ``ADV_CACHE_INCLUDE_PK`` to activate the \"primary key\" feature,\n default to ``False`` (``include_pk`` in the ``Meta`` class)\n- ``ADV_CACHE_BACKEND`` to choose the cache backend to use, default to\n ``\"default\"`` (``cache_backend`` in the ``Meta`` class)\n- ``ADV_CACHE_VERSION`` to create your own internal version (will be\n concatenated to the real internal version of\n ``django-adv-cache-tag``), default to ``\"\"`` (``internal_version`` in\n the ``Meta`` class)\n\nHow it works\n------------\n\nHere is a quick overview on how things work in ``django-adv-cache-tag``\n\nPartial caching\n~~~~~~~~~~~~~~~\n\nYour template :\n\n.. code:: django\n\n {% load adv_cache %}\n {% cache ... %}\n foo\n {% nocache %}\n bar\n {% endnocache %}\n baz\n {% endcache %}\n\nCached version (we ignore versioning and compress here, just to see how\nit works):\n\n.. code:: django\n\n foo\n {% endRAW_xyz %}\n bar\n {% RAW_xyz %}\n baz\n\nWhen cached version is loaded, we parse :\n\n.. code:: django\n\n {% RAW_xyz %}\n foo\n {% endRAW_xyz %}\n bar\n {% RAW_xyz %}\n baz\n {% endRAW_xyz %}\n\nThe first ``{% RAW_xyz %}`` and the last ``{% endRAW_xyz %}`` are not\nincluded in the cached version and added before parsing, only to save\nsome bytes.\n\nParts between ``{% RAW_xyz %}`` and ``{% endRAW_xyz %}`` are not parsed\nat all (seen as a ``TextNode`` by django)\n\nThe ``xyz`` part of the ``RAW`` and ``endRAW`` templatetags depends on\nthe ``SECRET_KEY`` and so is unique for a given site.\n\nIt allows to avoid at max the possible collisions with parsed content in\nthe cached version.\n\nWe could have used ``{% nocache %}`` and ``{% endnocache %}`` instead of\n``{% RAW_xyz %}`` and ``{% endRAW_xyz %}`` but in the parsed template,\nstored in the cache, if the html includes one of these strings, our\nfinal template would be broken, so we use long ones with a hash (but we\ncan not be sure at 100% these strings could not be in the cached html,\nbut for common usages it should suffice)\n\nLicense\n-------\n\n``django-adv-cache-tag`` is published under the MIT License (see the\nLICENSE file)\n\nRunning tests\n-------------\n\nIf ``adv_cache_tag`` is in the ``INSTALLED_APPS`` of your project, simply\nrun::\n\n django-admin test adv_cache_tag\n\n(you may want to use ``django-admin`` or ``./manage.py`` depending on\nyour installation)\n\nIf you are in a fresh virtualenv to work on ``adv_cache_tag``, install\nthe django version you want::\n\n pip install django\n\nThen make the ``adv_cache_tag`` module available in your python path.\nFor example, with ``virtualenv-wrapper``, considering you are at the\nroot of the ``django-adv-cache-tag`` repository, simply do::\n\n add2virtualenv .\n\nOr simply::\n\n pip install -e .\n\nThen to run the tests, this library provides a test project, so you can\nlaunch them this way::\n\n DJANGO_SETTINGS_MODULE=adv_cache_tag.tests.testproject.settings django-admin.py test adv_cache_tag\n\nOr simply launch the ``runtests.sh`` script (it will run this exact\ncommand)::\n\n ./runtests.sh\n\nSupported versions\n------------------\n\n============== ============== ===============\nDjango version Python version Library version\n============== ============== ===============\n1.7 to 1.11 2.7 0.4\n1.7 3.4 1.1.3\n1.8 to 1.10 3.4, 3.5 1.1.3\n1.11 3.4 to 3.6 1.1.3\n2.0 3.4, to 3.7 1.1.3\n2.1 3.5 to 3.7 1.1.3\n2.2 3.5 to 3.8 1.1.3\n3.0 3.6 to 3.8 1.1.3\n============== ============== ===============\n\nSupport for Python 2 is dropped since version 1 of ``django-adv-cache-tag``\n\n\n.. |PyPI Version| image:: https://img.shields.io/pypi/v/django-adv-cache-tag.png\n :target: https://pypi.python.org/pypi/django-adv-cache-tag\n :alt: PyPI Version\n.. |Build Status| image:: https://travis-ci.org/twidi/django-adv-cache-tag.png\n :target: https://travis-ci.org/twidi/django-adv-cache-tag\n :alt: Build Status on Travis CI\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "An advanced template tag for caching in django: versioning, compress, partial caching, easy inheritance",
"version": "1.1.5",
"project_urls": {
"Homepage": "https://github.com/erudit/django-adv-cache-tag"
},
"split_keywords": [
"django",
"cache",
"templatetag",
"template"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "6196e30ab7190903e1ca14a632b3c2714c1fbd93c5311c874b5a3c70e6e94473",
"md5": "9851d0d76c48f6affd94ca373c655d40",
"sha256": "4ce31df42368867d35b0a612b6ff744cbfdbd9d61459fb6613dda0f3968ad144"
},
"downloads": -1,
"filename": "erudit_django_adv_cache_tag-1.1.5-py3-none-any.whl",
"has_sig": false,
"md5_digest": "9851d0d76c48f6affd94ca373c655d40",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.4",
"size": 30688,
"upload_time": "2023-05-11T19:39:48",
"upload_time_iso_8601": "2023-05-11T19:39:48.994072Z",
"url": "https://files.pythonhosted.org/packages/61/96/e30ab7190903e1ca14a632b3c2714c1fbd93c5311c874b5a3c70e6e94473/erudit_django_adv_cache_tag-1.1.5-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "92794dfa45193223cb0421793786061b0a80de8dba460097bbc3e46232fb9fdf",
"md5": "fe026d88d243399e95ef6508744920fb",
"sha256": "3d695b43a97aa42692b76a26a334dd107d4e19224f6be1e5a5d26f66911fac7b"
},
"downloads": -1,
"filename": "erudit-django-adv-cache-tag-1.1.5.tar.gz",
"has_sig": false,
"md5_digest": "fe026d88d243399e95ef6508744920fb",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.4",
"size": 37513,
"upload_time": "2023-05-11T19:39:51",
"upload_time_iso_8601": "2023-05-11T19:39:51.789564Z",
"url": "https://files.pythonhosted.org/packages/92/79/4dfa45193223cb0421793786061b0a80de8dba460097bbc3e46232fb9fdf/erudit-django-adv-cache-tag-1.1.5.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-05-11 19:39:51",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "erudit",
"github_project": "django-adv-cache-tag",
"travis_ci": true,
"coveralls": false,
"github_actions": false,
"lcname": "erudit-django-adv-cache-tag"
}