Skip to content

Commit

Permalink
Refactoring to provide middleware (#12)
Browse files Browse the repository at this point in the history
* Refactoring to provide middleware
* Adding nocache_page decorator
  • Loading branch information
vsalvino authored Mar 18, 2019
1 parent 470e972 commit 1130136
Show file tree
Hide file tree
Showing 7 changed files with 286 additions and 122 deletions.
169 changes: 111 additions & 58 deletions docs/getting_started/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@ Add to installed apps in the project settings::
...
]

Enable the middleware. ``UpdateCacheMiddleware`` must come FIRST and ``FetchFromCacheMiddleware``
must come LAST in the list of middleware to correctly cache everything::

MIDDLEWARE = [
'wagtailcache.cache.UpdateCacheMiddleware',

...

'wagtailcache.cache.FetchFromCacheMiddleware',
]

Do not use the Wagtail Cache middleware with the Django cache middleware. If you are currently using
the Django cache middleware, you should remove it before adding the Wagtail Cache middleware.


2. Define a cache
-----------------
Expand All @@ -33,62 +47,7 @@ suitable for use on any web server::
}


3. Use the cache decorator
--------------------------

Finally, use the ``cache_page`` decorator on any views or URLs.

Caching pages
~~~~~~~~~~~~~

Most likely you will want this on all of your wagtail pages, so you will have to
replace the inclusion of ``wagtail_urls`` in your project's ``urls.py``. You will
need to change from this::

from django.conf.urls import url

url(r'', include(wagtail_urls)),

To this::

from django.conf.urls import url

from django.contrib.auth import views as auth_views
from wagtail.core.urls import serve_pattern, WAGTAIL_FRONTEND_LOGIN_TEMPLATE
from wagtail.core import views as wagtail_views
from wagtailcache.cache import cache_page

# Copied from wagtail.core.urls:
url(r'^_util/authenticate_with_password/(\d+)/(\d+)/$', wagtail_views.authenticate_with_password,
name='wagtailcore_authenticate_with_password'),
url(r'^_util/login/$', auth_views.LoginView.as_view(template_name=WAGTAIL_FRONTEND_LOGIN_TEMPLATE),
name='wagtailcore_login'),

# Wrap the serve function with wagtail-cache
url(serve_pattern, cache_page(wagtail_views.serve), name='wagtail_serve'),

Caching views
~~~~~~~~~~~~~

You can also use the decorator on views::

from wagtailcache.cache import cache_page

@cache_page
def myview(request):
...

To use it on class-based views::

from django.utils.decorators import method_decorator
from wagtailcache.cache import cache_page

@method_decorator(cache_page, name='dispatch')
class MyView(TemplateView):
...


4. Instruct pages how to cache (optional)
3. Instruct pages how to cache (optional)
-----------------------------------------

There are many situations where a specific page should not be cached. For example,
Expand Down Expand Up @@ -120,7 +79,7 @@ header via ``cache_control``, which can be a dynamic function or a string::
...


Setting this to ``no-cache`` or ``private`` will tell wagtail-cache **not** to cache this page.
Setting this to contain ``no-cache`` or ``private`` will tell wagtail-cache **not** to cache this page.
You could also set it to a custom value such as "public, max-age=3600". It can also be a function::

from wagtailcache.cache import WagtailCacheMixin
Expand All @@ -132,9 +91,37 @@ You could also set it to a custom value such as "public, max-age=3600". It can a

...

Regardless of the mixin, wagtail-cache will never cache a response that has a ``Cache-Control`` header
Regardless of the mixin, Wagtail Cache will never cache a response that has a ``Cache-Control`` header
containing ``no-cache`` or ``private``. Adding this header to any response will cause it to be skipped.

To explicitly not cache certain views or URL patterns, you could also wrap them with the ``nocache_page``
decorator, which adds the ``Cache-Control: no-cache`` header to all responses of that view or
URL pattern. To use with a view::

from wagtailcache.cache import nocache_page

@nocache_page
def myview(request):
...

Or on a URL pattern::

from wagtailcache.cache import nocache_page

...

url(r'^url/pattern/$', nocache_page(viewname), name='viewname'),

...

When using the Wagtail Cache middleware, the middleware will detect CSRF tokens and will only cache
those responses on a per-cookie basis. So Wagtail Cache should work well with CSRF tokens 🙂.
But if you still experience issues with CSRF tokens, use the mixin, the ``nocache_page`` decorator,
or set the ``Cache-Control`` header to ``no-cache`` on the response to guarantee that it will
never be cached. If you are using the ``cache_page`` decorator instead of the middleware, you
**must** use the mixin or set the ``Cache-Control`` header on responses with CSRF tokens to avoid
getting 403 forbidden errors.


Using a separate cache backend
------------------------------
Expand All @@ -152,3 +139,69 @@ so that purging the page cache will not affect other caches::
...
}
}


Only cache specific views
-------------------------

The wagtail-cache middleware will attempt to cache ALL responses that appear to be cacheable
(meaning the response does not contain a 'no-cache'/'private' Cache-Control header, the request method
is GET or HEAD, the response status code is 200, 301, 302, 404, the response did not set a cookie,
the page is not in preview mode, a user is not logged in, and many other requirements).

To only cache specific views, remove the middleware and use the ``cache_page`` decorator on views or URLs.

Alternatively, to continue using the middleware but explicitly not cache certain views or URLs, wrap those
views or URLs with the ``nocache_page`` decorator.

Note that when using the ``cache_page`` decorator, it is not possible to cache Wagtail page 404s or redirects. Only the
middleware is able to cache those responses.

Caching wagtail pages only
~~~~~~~~~~~~~~~~~~~~~~~~~~

Most likely you will want this on all of your wagtail pages, so you will have to
replace the inclusion of ``wagtail_urls`` in your project's ``urls.py``. You will
need to change from this::

from django.conf.urls import url

url(r'', include(wagtail_urls)),

To this::

from django.conf.urls import url

from django.contrib.auth import views as auth_views
from wagtail.core.urls import serve_pattern, WAGTAIL_FRONTEND_LOGIN_TEMPLATE
from wagtail.core import views as wagtail_views
from wagtailcache.cache import cache_page

# Copied from wagtail.core.urls:
url(r'^_util/authenticate_with_password/(\d+)/(\d+)/$', wagtail_views.authenticate_with_password,
name='wagtailcore_authenticate_with_password'),
url(r'^_util/login/$', auth_views.LoginView.as_view(template_name=WAGTAIL_FRONTEND_LOGIN_TEMPLATE),
name='wagtailcore_login'),

# Wrap the serve function with wagtail-cache
url(serve_pattern, cache_page(wagtail_views.serve), name='wagtail_serve'),

Caching views
~~~~~~~~~~~~~

You can also use the decorator on views::

from wagtailcache.cache import cache_page

@cache_page
def myview(request):
...

To use it on class-based views::

from django.utils.decorators import method_decorator
from wagtailcache.cache import cache_page

@method_decorator(cache_page, name='dispatch')
class MyView(TemplateView):
...
10 changes: 5 additions & 5 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
Wagtail Cache Documentation
===========================

Wagtail Cache is a simple page cache for Wagtail using the Django cache middleware.
It provides an extremely fast way of serving pages with no database hits whatsoever,
but is customizable enough that the developer can determine whether or not to cache
on a request by request basis.
Wagtail Cache is a simple page cache for Wagtail inspired by the Django cache middleware.
It provides an extremely fast way of serving pages with no database hits whatsoever.
It is customizable enough that the developer can determine whether or not to cache
on each individual request/response, or even on the Wagtail page models directly!

Wagtail Cache also provides a panel to show cache stats and clear the cache
from the wagtail admin under Settings > Cache.
Expand All @@ -14,7 +14,7 @@ Notes
-----

This cache feature was originally part of `coderedcms <https://github.com/coderedcorp/coderedcms>`_
and has been in use successfully on live production sites.
and is in use successfully on live production sites.

Requires Wagtail >= 2.0

Expand Down
1 change: 1 addition & 0 deletions docs/releases/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Release Notes
.. toctree::
:maxdepth: 1

v0.5.0
v0.4.0
v0.3.0
v0.2.1
Expand Down
9 changes: 9 additions & 0 deletions docs/releases/v0.5.0.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
wagtail-cache 0.5.0 release notes
=================================

* Added new middleware. This is now the recommended way of using Wagtail Cache. See :doc:`/getting_started/install`.
* The middleware will additionally cache 404 and 301/302 responses, lightening the load on your database.
* The middleware will intelligently handle CSRF tokens and only cache those responses based on the cookie.
So the new middleware should completely eliminate any CSRF token issues while also being able to cache those pages.
* The middleware now processes all cacheable requests/responses, not just wagtail pages. To revert to previous
behavior, continue using the decorator.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
author="CodeRed LLC",
author_email='[email protected]',
url='https://github.com/coderedcorp/wagtail-cache',
description="A simple page cache for Wagtail using the Django cache middleware.",
description="A simple page cache for Wagtail based on the Django cache middleware.",
long_description=readme,
long_description_content_type='text/markdown',
license="BSD license",
Expand Down
2 changes: 1 addition & 1 deletion wagtailcache/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
release = ['0', '4', '0']
release = ['0', '5', '0']
__version__ = "{0}.{1}.{2}".format(release[0], release[1], release[2])
__shortversion__ = "{0}.{1}".format(release[0], release[1])

Expand Down
Loading

0 comments on commit 1130136

Please sign in to comment.