mirror of
https://github.com/jazzband/django-downloadview.git
synced 2026-03-16 22:40:25 +00:00
Refs #2 - Introduced support of Lighttpd's X-Sendfile (mostly copied from django_downloadview.apache).
This commit is contained in:
parent
b5191c6a6f
commit
e33a8165ef
19 changed files with 383 additions and 22 deletions
|
|
@ -10,6 +10,7 @@ future releases, check `milestones`_ and :doc:`/about/vision`.
|
|||
|
||||
X-Sendfile support.
|
||||
|
||||
- Feature #2 - Introduced support of Lighttpd's x-Sendfile.
|
||||
- Feature #36 - Introduced support of Apache's mod_xsendfile.
|
||||
|
||||
|
||||
|
|
|
|||
1
demo/demoproject/lighttpd/__init__.py
Normal file
1
demo/demoproject/lighttpd/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
"""Lighttpd optimizations."""
|
||||
1
demo/demoproject/lighttpd/models.py
Normal file
1
demo/demoproject/lighttpd/models.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
"""Required to make a Django application."""
|
||||
43
demo/demoproject/lighttpd/tests.py
Normal file
43
demo/demoproject/lighttpd/tests.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import os
|
||||
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.urlresolvers import reverse
|
||||
import django.test
|
||||
|
||||
from django_downloadview.lighttpd import assert_x_sendfile
|
||||
|
||||
from demoproject.lighttpd.views import storage, storage_dir
|
||||
|
||||
|
||||
def setup_file():
|
||||
if not os.path.exists(storage_dir):
|
||||
os.makedirs(storage_dir)
|
||||
storage.save('hello-world.txt', ContentFile(u'Hello world!\n'))
|
||||
|
||||
|
||||
class OptimizedByMiddlewareTestCase(django.test.TestCase):
|
||||
def test_response(self):
|
||||
"""'lighttpd:optimized_by_middleware' returns X-Sendfile response."""
|
||||
setup_file()
|
||||
url = reverse('lighttpd:optimized_by_middleware')
|
||||
response = self.client.get(url)
|
||||
assert_x_sendfile(
|
||||
self,
|
||||
response,
|
||||
content_type="text/plain; charset=utf-8",
|
||||
basename="hello-world.txt",
|
||||
file_path="/lighttpd-optimized-by-middleware/hello-world.txt")
|
||||
|
||||
|
||||
class OptimizedByDecoratorTestCase(django.test.TestCase):
|
||||
def test_response(self):
|
||||
"""'lighttpd:optimized_by_decorator' returns X-Sendfile response."""
|
||||
setup_file()
|
||||
url = reverse('lighttpd:optimized_by_decorator')
|
||||
response = self.client.get(url)
|
||||
assert_x_sendfile(
|
||||
self,
|
||||
response,
|
||||
content_type="text/plain; charset=utf-8",
|
||||
basename="hello-world.txt",
|
||||
file_path="/lighttpd-optimized-by-decorator/hello-world.txt")
|
||||
13
demo/demoproject/lighttpd/urls.py
Normal file
13
demo/demoproject/lighttpd/urls.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
"""URL mapping."""
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
|
||||
urlpatterns = patterns(
|
||||
'demoproject.lighttpd.views',
|
||||
url(r'^optimized-by-middleware/$',
|
||||
'optimized_by_middleware',
|
||||
name='optimized_by_middleware'),
|
||||
url(r'^optimized-by-decorator/$',
|
||||
'optimized_by_decorator',
|
||||
name='optimized_by_decorator'),
|
||||
)
|
||||
22
demo/demoproject/lighttpd/views.py
Normal file
22
demo/demoproject/lighttpd/views.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.files.storage import FileSystemStorage
|
||||
|
||||
from django_downloadview import StorageDownloadView
|
||||
from django_downloadview.lighttpd import x_sendfile
|
||||
|
||||
|
||||
storage_dir = os.path.join(settings.MEDIA_ROOT, 'lighttpd')
|
||||
storage = FileSystemStorage(location=storage_dir,
|
||||
base_url=''.join([settings.MEDIA_URL, 'lighttpd/']))
|
||||
|
||||
|
||||
optimized_by_middleware = StorageDownloadView.as_view(storage=storage,
|
||||
path='hello-world.txt')
|
||||
|
||||
|
||||
optimized_by_decorator = x_sendfile(
|
||||
StorageDownloadView.as_view(storage=storage, path='hello-world.txt'),
|
||||
source_url=storage.base_url,
|
||||
destination_dir='/lighttpd-optimized-by-decorator/')
|
||||
|
|
@ -53,6 +53,7 @@ INSTALLED_APPS = (
|
|||
'demoproject.virtual', # Demo around VirtualDownloadView
|
||||
'demoproject.nginx', # Sample optimizations for Nginx X-Accel.
|
||||
'demoproject.apache', # Sample optimizations for Apache X-Sendfile.
|
||||
'demoproject.lighttpd', # Sample optimizations for Lighttpd X-Sendfile.
|
||||
# For test purposes. The demo project is part of django-downloadview
|
||||
# test suite.
|
||||
'django_nose',
|
||||
|
|
@ -74,6 +75,7 @@ MIDDLEWARE_CLASSES = [
|
|||
DOWNLOADVIEW_BACKEND = 'django_downloadview.nginx.XAccelRedirectMiddleware'
|
||||
"""Could also be:
|
||||
DOWNLOADVIEW_BACKEND = 'django_downloadview.apache.XSendfileMiddleware'
|
||||
DOWNLOADVIEW_BACKEND = 'django_downloadview.lighttpd.XSendfileMiddleware'
|
||||
"""
|
||||
DOWNLOADVIEW_RULES = [
|
||||
{
|
||||
|
|
@ -89,6 +91,15 @@ DOWNLOADVIEW_RULES = [
|
|||
# demonstrate usage of several backends.
|
||||
'backend': 'django_downloadview.apache.XSendfileMiddleware',
|
||||
},
|
||||
{
|
||||
'source_url': '/media/lighttpd/',
|
||||
'destination_dir': '/lighttpd-optimized-by-middleware/',
|
||||
# Bypass global default backend with additional argument "backend".
|
||||
# Notice that in general use case, ``DOWNLOADVIEW_BACKEND`` should be
|
||||
# enough. Here, the django_downloadview demo project needs to
|
||||
# demonstrate usage of several backends.
|
||||
'backend': 'django_downloadview.lighttpd.XSendfileMiddleware',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ urlpatterns = patterns(
|
|||
url(r'^apache/', include('demoproject.apache.urls',
|
||||
app_name='apache',
|
||||
namespace='apache')),
|
||||
# Lighttpd optimizations.
|
||||
url(r'^lighttpd/', include('demoproject.lighttpd.urls',
|
||||
app_name='lighttpd',
|
||||
namespace='lighttpd')),
|
||||
# An informative homepage.
|
||||
url(r'$', home, name='home')
|
||||
)
|
||||
|
|
|
|||
14
django_downloadview/lighttpd/__init__.py
Normal file
14
django_downloadview/lighttpd/__init__.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Optimizations for Lighttpd.
|
||||
|
||||
See also `documentation of X-Sendfile for Lighttpd
|
||||
<http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file>`_ and
|
||||
:doc:`narrative documentation about Lighttpd optimizations
|
||||
</optimizations/lighttpd>`.
|
||||
|
||||
"""
|
||||
# API shortcuts.
|
||||
from django_downloadview.lighttpd.decorators import x_sendfile # NoQA
|
||||
from django_downloadview.lighttpd.response import XSendfileResponse # NoQA
|
||||
from django_downloadview.lighttpd.tests import assert_x_sendfile # NoQA
|
||||
from django_downloadview.lighttpd.middlewares import XSendfileMiddleware # NoQA
|
||||
16
django_downloadview/lighttpd/decorators.py
Normal file
16
django_downloadview/lighttpd/decorators.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Decorators to apply Lighttpd X-Sendfile on a specific view."""
|
||||
from django_downloadview.decorators import DownloadDecorator
|
||||
from django_downloadview.lighttpd.middlewares import XSendfileMiddleware
|
||||
|
||||
|
||||
def x_sendfile(view_func, *args, **kwargs):
|
||||
"""Apply
|
||||
:class:`~django_downloadview.lighttpd.middlewares.XSendfileMiddleware` to
|
||||
``view_func``.
|
||||
|
||||
Proxies (``*args``, ``**kwargs``) to middleware constructor.
|
||||
|
||||
"""
|
||||
decorator = DownloadDecorator(XSendfileMiddleware)
|
||||
return decorator(view_func, *args, **kwargs)
|
||||
30
django_downloadview/lighttpd/middlewares.py
Normal file
30
django_downloadview/lighttpd/middlewares.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
from django_downloadview.lighttpd.response import XSendfileResponse
|
||||
from django_downloadview.middlewares import (ProxiedDownloadMiddleware,
|
||||
NoRedirectionMatch)
|
||||
|
||||
|
||||
class XSendfileMiddleware(ProxiedDownloadMiddleware):
|
||||
"""Configurable middleware, for use in decorators or in global middlewares.
|
||||
|
||||
Standard Django middlewares are configured globally via settings. Instances
|
||||
of this class are to be configured individually. It makes it possible to
|
||||
use this class as the factory in
|
||||
:py:class:`django_downloadview.decorators.DownloadDecorator`.
|
||||
|
||||
"""
|
||||
def __init__(self, source_dir=None, source_url=None, destination_dir=None):
|
||||
"""Constructor."""
|
||||
super(XSendfileMiddleware, self).__init__(source_dir,
|
||||
source_url,
|
||||
destination_dir)
|
||||
|
||||
def process_download_response(self, request, response):
|
||||
"""Replace DownloadResponse instances by XSendfileResponse ones."""
|
||||
try:
|
||||
redirect_url = self.get_redirect_url(response)
|
||||
except NoRedirectionMatch:
|
||||
return response
|
||||
return XSendfileResponse(file_path=redirect_url,
|
||||
content_type=response['Content-Type'],
|
||||
basename=response.basename,
|
||||
attachment=response.attachment)
|
||||
18
django_downloadview/lighttpd/response.py
Normal file
18
django_downloadview/lighttpd/response.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Lighttpd's specific responses."""
|
||||
import os.path
|
||||
|
||||
from django_downloadview.response import (ProxiedDownloadResponse,
|
||||
content_disposition)
|
||||
|
||||
|
||||
class XSendfileResponse(ProxiedDownloadResponse):
|
||||
"Delegates serving file to Lighttpd via X-Sendfile header."
|
||||
def __init__(self, file_path, content_type, basename=None,
|
||||
attachment=True):
|
||||
"""Return a HttpResponse with headers for Lighttpd X-Sendfile."""
|
||||
super(XSendfileResponse, self).__init__(content_type=content_type)
|
||||
if attachment:
|
||||
self.basename = basename or os.path.basename(file_path)
|
||||
self['Content-Disposition'] = content_disposition(self.basename)
|
||||
self['X-Sendfile'] = file_path
|
||||
28
django_downloadview/lighttpd/tests.py
Normal file
28
django_downloadview/lighttpd/tests.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import django_downloadview.apache.tests
|
||||
from django_downloadview.lighttpd.response import XSendfileResponse
|
||||
|
||||
|
||||
class XSendfileValidator(django_downloadview.apache.tests.XSendfileValidator):
|
||||
"""Utility class to validate XSendfileResponse instances.
|
||||
|
||||
See also :py:func:`assert_x_sendfile` shortcut function.
|
||||
|
||||
"""
|
||||
def assert_x_sendfile_response(self, test_case, response):
|
||||
test_case.assertTrue(isinstance(response, XSendfileResponse))
|
||||
|
||||
|
||||
def assert_x_sendfile(test_case, response, **assertions):
|
||||
"""Make ``test_case`` assert that ``response`` is a XSendfileResponse.
|
||||
|
||||
Optional ``assertions`` dictionary can be used to check additional items:
|
||||
|
||||
* ``basename``: the basename of the file in the response.
|
||||
|
||||
* ``content_type``: the value of "Content-Type" header.
|
||||
|
||||
* ``file_path``: the value of "X-Sendfile" header.
|
||||
|
||||
"""
|
||||
validator = XSendfileValidator()
|
||||
return validator(test_case, response, **assertions)
|
||||
|
|
@ -70,6 +70,24 @@ class APITestCase(unittest.TestCase):
|
|||
'assert_x_accel_redirect']
|
||||
self.assert_module_attributes('django_downloadview.nginx', api)
|
||||
|
||||
def test_apache_attributes(self):
|
||||
"""Apache-related API is exposed in django_downloadview.apache."""
|
||||
api = [
|
||||
'XSendfileResponse',
|
||||
'XSendfileMiddleware',
|
||||
'x_sendfile',
|
||||
'assert_x_sendfile']
|
||||
self.assert_module_attributes('django_downloadview.apache', api)
|
||||
|
||||
def test_lighttpd_attributes(self):
|
||||
"""Lighttpd-related API is exposed in django_downloadview.lighttpd."""
|
||||
api = [
|
||||
'XSendfileResponse',
|
||||
'XSendfileMiddleware',
|
||||
'x_sendfile',
|
||||
'assert_x_sendfile']
|
||||
self.assert_module_attributes('django_downloadview.lighttpd', api)
|
||||
|
||||
|
||||
class DeprecatedAPITestCase(django.test.SimpleTestCase):
|
||||
"""Make sure using deprecated items raise DeprecationWarning."""
|
||||
|
|
|
|||
|
|
@ -51,20 +51,20 @@ Example:
|
|||
|
||||
.. literalinclude:: /../demo/demoproject/settings.py
|
||||
:language: python
|
||||
:lines: 63-70
|
||||
:lines: 64-71
|
||||
|
||||
Then set ``django_downloadview.apache.XSendfileMiddleware`` as
|
||||
``DOWNLOADVIEW_BACKEND``:
|
||||
|
||||
.. literalinclude:: /../demo/demoproject/settings.py
|
||||
:language: python
|
||||
:lines: 76
|
||||
:lines: 77
|
||||
|
||||
Then register as many ``DOWNLOADVIEW_RULES`` as you wish:
|
||||
|
||||
.. literalinclude:: /../demo/demoproject/settings.py
|
||||
:language: python
|
||||
:lines: 78, 79-82, 92
|
||||
:lines: 80, 85-87, 93, 103
|
||||
|
||||
Each item in ``DOWNLOADVIEW_RULES`` is a dictionary of keyword arguments passed
|
||||
to the middleware factory. In the example above, we capture responses by
|
||||
|
|
|
|||
|
|
@ -19,19 +19,19 @@ Supported features grid
|
|||
Supported features depend on backend. Given the file you want to stream, the
|
||||
backend may or may not be able to handle it:
|
||||
|
||||
+-----------------------+-------------------------+-------------------------+
|
||||
| View / File | :doc:`nginx` | :doc:`apache` |
|
||||
+=======================+=========================+=========================+
|
||||
| :doc:`/views/path` | Yes, local filesystem. | Yes, local filesystem. |
|
||||
+-----------------------+-------------------------+-------------------------+
|
||||
| :doc:`/views/storage` | Yes, local and remote. | Yes, local filesystem. |
|
||||
+-----------------------+-------------------------+-------------------------+
|
||||
| :doc:`/views/object` | Yes, local and remote. | Yes, local filesystem. |
|
||||
+-----------------------+-------------------------+-------------------------+
|
||||
| :doc:`/views/http` | Yes. | No. |
|
||||
+-----------------------+-------------------------+-------------------------+
|
||||
| :doc:`/views/virtual` | No. | No. |
|
||||
+-----------------------+-------------------------+-------------------------+
|
||||
+-----------------------+-------------------------+-------------------------+-------------------------+
|
||||
| View / File | :doc:`nginx` | :doc:`apache` | :doc:`lighttpd` |
|
||||
+=======================+=========================+=========================+=========================+
|
||||
| :doc:`/views/path` | Yes, local filesystem. | Yes, local filesystem. | Yes, local filesystem. |
|
||||
+-----------------------+-------------------------+-------------------------+-------------------------+
|
||||
| :doc:`/views/storage` | Yes, local and remote. | Yes, local filesystem. | Yes, local filesystem. |
|
||||
+-----------------------+-------------------------+-------------------------+-------------------------+
|
||||
| :doc:`/views/object` | Yes, local and remote. | Yes, local filesystem. | Yes, local filesystem. |
|
||||
+-----------------------+-------------------------+-------------------------+-------------------------+
|
||||
| :doc:`/views/http` | Yes. | No. | No. |
|
||||
+-----------------------+-------------------------+-------------------------+-------------------------+
|
||||
| :doc:`/views/virtual` | No. | No. | No. |
|
||||
+-----------------------+-------------------------+-------------------------+-------------------------+
|
||||
|
||||
As an example, :doc:`Nginx X-Accel </optimizations/nginx>` handles URL for
|
||||
internal redirects, so it can manage
|
||||
|
|
@ -77,6 +77,7 @@ Here are optimizations builtin `django_downloadview`:
|
|||
|
||||
nginx
|
||||
apache
|
||||
lighttpd
|
||||
|
||||
.. note:: If you need support for additional optimizations, `tell us`_!
|
||||
|
||||
|
|
|
|||
140
docs/optimizations/lighttpd.txt
Normal file
140
docs/optimizations/lighttpd.txt
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
########
|
||||
Lighttpd
|
||||
########
|
||||
|
||||
If you serve Django behind `Lighttpd`, then you can delegate the file streaming
|
||||
to `Lighttpd` and get increased performance:
|
||||
|
||||
* lower resources used by Python/Django workers ;
|
||||
* faster download.
|
||||
|
||||
See `Lighttpd X-Sendfile documentation`_ for details.
|
||||
|
||||
.. note::
|
||||
|
||||
Currently, `django_downloadview` supports ``X-Sendfile``, but not
|
||||
``X-Sendfile2``. If you need ``X-Sendfile2`` or know how to handle it,
|
||||
check `X-Sendfile2 feature request on django_downloadview's bugtracker`_.
|
||||
|
||||
|
||||
*****************
|
||||
Known limitations
|
||||
*****************
|
||||
|
||||
* Lighttpd needs access to the resource by path on local filesystem.
|
||||
* Thus only files that live on local filesystem can be streamed by Lighttpd.
|
||||
|
||||
|
||||
************
|
||||
Given a view
|
||||
************
|
||||
|
||||
Let's consider the following view:
|
||||
|
||||
.. literalinclude:: /../demo/demoproject/lighttpd/views.py
|
||||
:language: python
|
||||
:lines: 1-6, 8-16
|
||||
|
||||
What is important here is that the files will have an ``url`` property
|
||||
implemented by storage. Let's setup an optimization rule based on that URL.
|
||||
|
||||
.. note::
|
||||
|
||||
It is generally easier to setup rules based on URL rather than based on
|
||||
name in filesystem. This is because path is generally relative to storage,
|
||||
whereas URL usually contains some storage identifier, i.e. it is easier to
|
||||
target a specific location by URL rather than by filesystem name.
|
||||
|
||||
|
||||
***************************
|
||||
Setup XSendfile middlewares
|
||||
***************************
|
||||
|
||||
Make sure ``django_downloadview.SmartDownloadMiddleware`` is in
|
||||
``MIDDLEWARE_CLASSES`` of your `Django` settings.
|
||||
|
||||
Example:
|
||||
|
||||
.. literalinclude:: /../demo/demoproject/settings.py
|
||||
:language: python
|
||||
:lines: 64-71
|
||||
|
||||
Then set ``django_downloadview.lighttpd.XSendfileMiddleware`` as
|
||||
``DOWNLOADVIEW_BACKEND``:
|
||||
|
||||
.. literalinclude:: /../demo/demoproject/settings.py
|
||||
:language: python
|
||||
:lines: 78
|
||||
|
||||
Then register as many ``DOWNLOADVIEW_RULES`` as you wish:
|
||||
|
||||
.. literalinclude:: /../demo/demoproject/settings.py
|
||||
:language: python
|
||||
:lines: 80, 94-96, 102, 103
|
||||
|
||||
Each item in ``DOWNLOADVIEW_RULES`` is a dictionary of keyword arguments passed
|
||||
to the middleware factory. In the example above, we capture responses by
|
||||
``source_url`` and convert them to internal redirects to ``destination_dir``.
|
||||
|
||||
.. autoclass:: django_downloadview.lighttpd.middlewares.XSendfileMiddleware
|
||||
:members:
|
||||
:inherited-members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:member-order: bysource
|
||||
|
||||
|
||||
****************************************
|
||||
Per-view setup with x_sendfile decorator
|
||||
****************************************
|
||||
|
||||
Middlewares should be enough for most use cases, but you may want per-view
|
||||
configuration. For `Lighttpd`, there is ``x_sendfile``:
|
||||
|
||||
.. autofunction:: django_downloadview.lighttpd.decorators.x_sendfile
|
||||
|
||||
As an example:
|
||||
|
||||
.. literalinclude:: /../demo/demoproject/lighttpd/views.py
|
||||
:language: python
|
||||
:lines: 1-7, 17-
|
||||
|
||||
|
||||
*************************************
|
||||
Test responses with assert_x_sendfile
|
||||
*************************************
|
||||
|
||||
Use :func:`~django_downloadview.lighttpd.decorators.assert_x_sendfile`
|
||||
function as a shortcut in your tests.
|
||||
|
||||
.. literalinclude:: /../demo/demoproject/lighttpd/tests.py
|
||||
:language: python
|
||||
|
||||
.. autofunction:: django_downloadview.lighttpd.tests.assert_x_sendfile
|
||||
|
||||
The tests above assert the `Django` part is OK. Now let's configure `Lighttpd`.
|
||||
|
||||
|
||||
**************
|
||||
Setup Lighttpd
|
||||
**************
|
||||
|
||||
See `Lighttpd X-Sendfile documentation`_ for details.
|
||||
|
||||
|
||||
*********************************************
|
||||
Assert everything goes fine with healthchecks
|
||||
*********************************************
|
||||
|
||||
:doc:`Healthchecks </healthchecks>` are the best way to check the complete
|
||||
setup.
|
||||
|
||||
|
||||
.. rubric:: References
|
||||
|
||||
.. target-notes::
|
||||
|
||||
.. _`Lighttpd X-Sendfile documentation`:
|
||||
http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file
|
||||
.. _`X-Sendfile2 feature request on django_downloadview's bugtracker`:
|
||||
https://github.com/benoitbryon/django-downloadview/issues/67
|
||||
|
|
@ -52,20 +52,20 @@ Example:
|
|||
|
||||
.. literalinclude:: /../demo/demoproject/settings.py
|
||||
:language: python
|
||||
:lines: 63-70
|
||||
:lines: 64-71
|
||||
|
||||
Then set ``django_downloadview.nginx.XAccelRedirectMiddleware`` as
|
||||
``DOWNLOADVIEW_BACKEND``:
|
||||
|
||||
.. literalinclude:: /../demo/demoproject/settings.py
|
||||
:language: python
|
||||
:lines: 74
|
||||
:lines: 75
|
||||
|
||||
Then register as many ``DOWNLOADVIEW_RULES`` as you wish:
|
||||
|
||||
.. literalinclude:: /../demo/demoproject/settings.py
|
||||
:language: python
|
||||
:lines: 78, 79-82, 92
|
||||
:lines: 80, 81-84, 103
|
||||
|
||||
Each item in ``DOWNLOADVIEW_RULES`` is a dictionary of keyword arguments passed
|
||||
to the middleware factory. In the example above, we capture responses by
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ Example:
|
|||
|
||||
.. literalinclude:: /../demo/demoproject/settings.py
|
||||
:language: python
|
||||
:lines: 63-70
|
||||
:lines: 64-71
|
||||
|
||||
|
||||
********************
|
||||
|
|
@ -43,7 +43,7 @@ Example:
|
|||
|
||||
.. literalinclude:: /../demo/demoproject/settings.py
|
||||
:language: python
|
||||
:lines: 74
|
||||
:lines: 75
|
||||
|
||||
See :doc:`/optimizations/index` for a list of available backends (middlewares).
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ Here is an example containing one rule using keyword arguments:
|
|||
|
||||
.. literalinclude:: /../demo/demoproject/settings.py
|
||||
:language: python
|
||||
:lines: 78, 79-82, 92
|
||||
:lines: 80, 81-84, 103
|
||||
|
||||
See :doc:`/optimizations/index` for details about builtin backends
|
||||
(middlewares) and their options.
|
||||
|
|
|
|||
Loading…
Reference in a new issue