File: index.rst

package info (click to toggle)
python-django-cache-machine 1.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid, trixie
  • size: 228 kB
  • sloc: python: 1,143; makefile: 74
file content (224 lines) | stat: -rw-r--r-- 7,882 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
.. _caching:

=============
Cache Machine
=============

Cache Machine provides automatic caching and invalidation for Django models
through the ORM.  The code is hosted on
`github <http://github.com/django-cache-machine/django-cache-machine>`_.

For an overview of new features and backwards-incompatible changes which may
affect you, please see the :ref:`release-notes`.

Settings
--------

Older versions of Cache Machine required you to use customized cache backends. These are no longer
needed and they have been removed from Cache Machine. Use the standard Django cache backends.

COUNT queries
^^^^^^^^^^^^^

Calls to ``QuerySet.count()`` can be cached, but they cannot be reliably
invalidated.  Cache Machine would have to do a full select to figure out the
object keys, which is probably much more data than you want to pull.  I
recommend a short cache timeout; long enough to avoid repetitive queries, but
short enough that stale counts won't be a big deal.  ::

    CACHE_COUNT_TIMEOUT = 60  # seconds, not too long.

By default, calls to ``QuerySet.count()`` are not cached. They are only cached
if ``CACHE_COUNT_TIMEOUT`` is set to a value other than
``caching.base.NO_CACHE``.

Empty querysets
^^^^^^^^^^^^^^^

By default cache machine will not cache empty querysets. To cache them::

    CACHE_EMPTY_QUERYSETS = True

.. _object-creation:

Object creation
^^^^^^^^^^^^^^^

By default Cache Machine does not invalidate queries when a new object is
created, because it can be expensive to maintain a flush list of all the
queries associated with a given table and cause significant disruption on
high-volume sites when *all* the queries for a particular model are
invalidated at once. If these are not issues for your site and immediate
inclusion of created objects in previously cached queries is desired, you
can enable this feature as follows::

    CACHE_INVALIDATE_ON_CREATE = 'whole-model'

Cache Manager
-------------

To enable caching for a model, add the :class:`~caching.base.CachingManager` to
that class and inherit from the :class:`~caching.base.CachingMixin`.  If you
want related lookups (foreign keys) to hit the cache, ``CachingManager`` must
be the default manager.  If you have multiple managers that should be cached,
return a :class:`~caching.base.CachingQuerySet` from the other manager's
``get_queryset`` method instead of subclassing ``CachingManager``, since that
would hook up the post_save and post_delete signals multiple times.

Here's what a minimal cached model looks like::

    from django.db import models

    from caching.base import CachingManager, CachingMixin

    class Zomg(CachingMixin, models.Model):
        val = models.IntegerField()

        objects = CachingManager()

        # if you use Django 2.0 or later, you must set base_manager_name
        class Meta:
            base_manager_name = 'objects'  # Attribute name of CachingManager(), above

Whenever you run a query, ``CachingQuerySet`` will try to find that query in
the cache.  Queries are keyed by ``{prefix}:{sql}``. If it's there, we return
the cached result set and everyone is happy.  If the query isn't in the cache,
the normal codepath to run a database query is executed.  As the objects in the
result set are iterated over, they are added to a list that will get cached
once iteration is done.

.. note::
    Nothing will be cached if the QuerySet is not iterated through completely.

Caching is supported for normal :class:`QuerySets <django.db.models.QuerySet>` and
for :meth:`django.db.models.Manager.raw`.  At this time, caching has not been
implemented for ``QuerySet.values`` or ``QuerySet.values_list``.

To support easy cache invalidation, we use "flush lists" to mark the cached
queries an object belongs to.  That way, all queries where an object was found
will be invalidated when that object changes.  Flush lists map an object key to
a list of query keys.

When an object is saved or deleted, all query keys in its flush list will be
deleted.  In addition, the flush lists of its foreign key relations will be
cleared.  To avoid stale foreign key relations, any cached objects will be
flushed when the object their foreign key points to is invalidated.

During cache invalidation, we explicitly set a None value instead of just
deleting so we don't have any race conditions where:

 * Thread 1 -> Cache miss, get object from DB
 * Thread 2 -> Object saved, deleted from cache
 * Thread 1 -> Store (stale) object fetched from DB in cache

The foundations of this module were derived from `Mike Malone's`_
`django-caching`_.

.. _`Mike Malone's`: http://immike.net/
.. _django-caching: http://github.com/mmalone/django-caching/

Changing the timeout of a CachingQuerySet instance
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

By default, the timeout for a ``CachingQuerySet`` instance will be the timeout
of the underlying cache being used by Cache Machine. To change the timeout of
a ``CachingQuerySet`` instance, you can assign a different value to the
``timeout`` attribute which represents the number of seconds to cache for

For example::

    def get_objects(name):
        qs = CachedClass.objects.filter(name=name)
        qs.timeout = 5  # seconds
        return qs

To disable caching for a particular ``CachingQuerySet`` instance, set the
``timeout`` attribute to ``caching.base.NO_CACHE``.

Manual Caching
--------------

Some things can be cached better outside of the ORM, so Cache Machine provides
the function :func:`caching.base.cached` for caching arbitrary objects.  Using
this function gives you more control over what gets cached, and for how long,
while abstracting a few repetitive elements.

.. autofunction:: caching.base.cached


Template Caching
----------------

Cache Machine includes a Jinja2 extension to cache template fragments based on
a queryset or cache-aware object.  These fragments will get invalidated on
using the same rules as ``CachingQuerySets``.

First, add it to your template environment::

    env = jinja2.Environment(extensions=['caching.ext.cache'])

.. highlight:: jinja

Now wrap all your queryset looping with the ``cache`` tag. ::

    {% cache objects %}  {# objects is a CachingQuerySet #}
      {% for obj in objects %}
        ...
      {% endfor %}
    {% endcache %}

...and for caching by single objects::

    {% cache object %}
      ...expensive processing...
    {% endcache %}

The tag can take an optional timeout. ::

    {% cache objects, 500 %}

.. highlight:: python

If someone wants to write a template tag for Django templates, I'd love to add
it.


Redis Support
-------------

Cache Machine support storing flush lists in Redis rather than memcached, which
is more efficient because Redis can manipulate the lists on the server side
rather than having to transfer the entire list back and forth for each
modification.

To enable Redis support for Cache Machine, add the following to your settings
file, replacing ``localhost`` with the hostname of your Redis server::

    CACHE_MACHINE_USE_REDIS = True
    REDIS_BACKEND = 'redis://localhost:6379'

.. note::
    When using Redis, memcached is still used for caching model objects, i.e.,
    only the flush lists are stored in Redis. You still need to configure
    ``CACHES`` the way you would normally for Cache Machine.


Classes That May Interest You
-----------------------------

.. autoclass:: caching.base.CachingModelIterable

.. autoclass:: caching.base.CachingManager
    :members:

    This :class:`manager <django.db.models.Manager>` always returns a
    :class:`~caching.CachingQuerySet`, and hooks up ``post_save`` and
    ``post_delete`` signals to invalidate caches.

.. autoclass:: caching.base.CachingMixin
    :members:

.. class:: caching.base.CachingQuerySet

    Overrides the default :class:`~django.db.models.QuerySet` to fetch objects
    from cache before hitting the database.