Reviewed documentation. Preparing first public release.

This commit is contained in:
Benoît Bryon 2012-12-04 11:47:37 +01:00
parent 7738d1c0b6
commit 4f29852ddb
27 changed files with 535 additions and 197 deletions

View file

@ -2,4 +2,9 @@
Authors & contributors
######################
* Benoit Bryon <benoit@marmelune.net>
Original code by `Novapost <http://www.novapost.fr>`_ team:
* Nicolas Tobo <https://github.com/nicolastobo>
* Lauréline Guérin <https://github.com/zebuline>
* Gregory Tappero <https://github.com/coulix>
* Benoît Bryon <benoit@marmelune.net>

View file

@ -0,0 +1,9 @@
Changelog
=========
1.0 (unreleased)
----------------
- Introduced optimizations for Nginx X-Accel: a middleware and a decorator
- Introduced generic views: DownloadView and ObjectDownloadView
- Initialized project

25
INSTALL
View file

@ -0,0 +1,25 @@
############
Installation
############
This project is open-source, published under BSD license.
See :doc:`/about/license` for details.
If you want to install a development environment, you should go to :doc:`/dev`
documentation.
Install the package with your favorite Python installer. As an example, with
pip:
.. code-block:: sh
pip install django-downloadview
There is no need to register this application in your Django's
``INSTALLED_APPS`` setting.
Next, you'll have to setup some download view(s). See :doc:`demo project
<demo>` for examples, and :doc:`API documentation <api/django_downloadview>`.
Optionally, you may setup additional :doc:`server optimizations
<optimizations/index>`.

View file

@ -1,5 +1,4 @@
recursive-include django_downloadview *
global-exclude *.pyc .*.swp
include *.txt
include *.rst
include AUTHORS CHANGELOG INSTALL LICENSE README VERSION

View file

@ -4,37 +4,29 @@ SHELL = /bin/bash
PROJECT = 'django-downloadview'
ROOT_DIR = $(shell pwd)
DATA_DIR = $(ROOT_DIR)/var
VIRTUALENV = virtualenv
VIRTUALENV_DIR = $(ROOT_DIR)/lib/virtualenv
PIP = $(VIRTUALENV_DIR)/bin/pip
WGET = wget
PYTHON = python
BUILDOUT_BOOTSTRAP_URL = https://raw.github.com/buildout/buildout/1.6.3/bootstrap/bootstrap.py
BUILDOUT_BOOTSTRAP = $(ROOT_DIR)/lib/buildout/bootstrap.py
BUILDOUT = $(ROOT_DIR)/bin/buildout
BUILDOUT_ARGS = -N
virtualenv:
if [ ! -x $(PIP) ]; then \
if [[ "`$(VIRTUALENV) --version`" < "`echo '1.8'`" ]]; then \
$(VIRTUALENV) --no-site-packages --distribute $(VIRTUALENV_DIR); \
else \
$(VIRTUALENV) $(VIRTUALENV_DIR); \
fi; \
$(PIP) install -U pip; \
fi
buildout:
# Install zc.buildout.
if [ ! -x $(BUILDOUT) ]; then \
# Download zc.buildout bootstrap.
if [ ! -f $(BUILDOUT_BOOTSTRAP) ]; then \
mkdir -p $(ROOT_DIR)/lib/buildout; \
$(PIP) install zc.buildout; \
mkdir -p `dirname $(BUILDOUT)`; \
ln -s $(VIRTUALENV_DIR)/bin/buildout $(BUILDOUT); \
$(WGET) $(BUILDOUT_BOOTSTRAP_URL) -O $(BUILDOUT_BOOTSTRAP); \
fi
# Bootstrap buildout.
if [ ! -f $(BUILDOUT) ]; then \
$(PYTHON) $(BUILDOUT_BOOTSTRAP) --distribute; \
fi
# Run zc.buildout.
$(BUILDOUT) $(BUILDOUT_ARGS)
develop: virtualenv buildout
develop: buildout
update: develop
@ -43,11 +35,11 @@ update: develop
clean:
find $(ROOT_DIR)/ -name "*.pyc" -delete
find $(ROOT_DIR)/ -name ".noseids" -delete
rm nosetests.xml
distclean: clean
rm -rf $(ROOT_DIR)/*.egg-info
rm -rf $(ROOT_DIR)/demo/*.egg-info
maintainer-clean: distclean
@ -60,7 +52,9 @@ test:
apidoc:
cp docs/api/index.txt docs/api-backup.txt
rm -rf docs/api/*
mv docs/api-backup.txt docs/api/index.txt
bin/sphinx-apidoc --suffix txt --output-dir $(ROOT_DIR)/docs/api django_downloadview
@ -71,5 +65,12 @@ sphinx:
documentation: apidoc sphinx
runserver:
bin/demo runserver
demo: develop runserver
release:
bin/fullrelease

5
README
View file

@ -4,11 +4,6 @@ Django-DownloadView
Django-DownloadView provides (class-based) generic download views for Django.
.. warning::
This project is experimental. It may be renamed or modified without
notices.
Example, in some urls.py:
.. code-block:: python

View file

@ -1 +1 @@
0.1
1.0dev

View file

@ -19,17 +19,16 @@ parts =
django-downloadview
directories
releaser
eggs =
[django-downloadview]
recipe = z3c.recipe.scripts
eggs =
django-downloadview-demo
bpython
nose
rednose
coverage
sphinx
[django-downloadview]
recipe = z3c.recipe.scripts
eggs = ${buildout:eggs}
initialization =
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'demoproject.settings'
@ -46,7 +45,7 @@ recipe = z3c.recipe.scripts
eggs = zest.releaser
[versions]
Django = 1.4.1
Django = 1.4.2
Jinja2 = 2.6
Sphinx = 1.1.3
bpython = 0.11

View file

@ -1,5 +1,60 @@
############################
Demo for Django-DownloadView
############################
############
Demo project
############
This is a demo project to illustrate (and test) Django-DownloadView usage.
The :file:`demo/` folder holds a demo project to illustrate (and test)
django-downloadview usage.
***********************
Browse demo code online
***********************
See `demo folder in project's repository`_.
***************
Deploy the demo
***************
System requirements:
* `Python`_ version 2.6 or 2.7, available as ``python`` command.
.. note::
You may use `Virtualenv`_ to make sure the active ``python`` is the right
one.
* ``make`` and ``wget`` to use the provided :file:`Makefile`.
Execute:
.. code-block:: sh
git clone git@github.com:benoitbryon/django-downloadview.git
cd django-downloadview/
make demo
It installs and runs the demo server on localhost, port 8000. So have a look
at http://localhost:8000/
.. note::
If you cannot execute the Makefile, read it and adapt the few commands it
contains to your needs.
Browse and use :file:`demo/demoproject/` as a sandbox.
**********
References
**********
.. target-notes::
.. _`demo folder in project's repository`:
https://github.com/benoitbryon/django-downloadview/tree/master/demo/demoproject/
.. _`Python`: http://python.org
.. _`Virtualenv`: http://virtualenv.org

View file

@ -2,5 +2,8 @@
from django_downloadview.views import DownloadView, ObjectDownloadView
#: Implement :pep:`396`
__version__ = '0.1'
pkg_resources = __import__('pkg_resources')
distribution = pkg_resources.get_distribution('django-downloadview')
#: Module version, as defined in PEP-0396.
__version__ = distribution.version

View file

@ -1,4 +1,9 @@
"""View decorators."""
"""View decorators.
See also decorators provided by server-specific modules, such as
:py:func:`django_downloadview.nginx.x_accel_redirect`.
"""
class DownloadDecorator(object):

View file

@ -3,17 +3,24 @@ from django_downloadview.response import is_download_response
class BaseDownloadMiddleware(object):
"""Base (abstract) Django middleware that process download responses.
"""Base (abstract) Django middleware that handles download responses.
Subclasses **must** implement ``process_download_response`` method.
Subclasses **must** implement :py:meth:`process_download_response` method.
"""
def is_download_response(self, response):
"""Return True if ``response`` can be considered as a file download."""
"""Return True if ``response`` can be considered as a file download.
By default, this method uses
:py:func:`django_downloadview.response.is_download_response`.
Override this method if you want a different behaviour.
"""
return is_download_response(response)
def process_response(self, request, response):
"""Call ``process_download_response()`` if ``response`` is download."""
"""Call :py:meth:`process_download_response` if ``response`` is
download."""
if self.is_download_response(response):
return self.process_download_response(request, response)
return response

View file

@ -1,6 +1,8 @@
"""Let Nginx serve files for increased performance.
"""Optimizations for Nginx.
See `Nginx X-accel documentation <http://wiki.nginx.org/X-accel>`_.
See also `Nginx X-accel documentation <http://wiki.nginx.org/X-accel>`_ and
:doc:`narrative documentation about Nginx optimizations
</optimizations/nginx>`.
"""
from datetime import datetime, timedelta
@ -15,6 +17,16 @@ from django_downloadview.utils import content_type_to_charset
#: Default value for X-Accel-Buffering header.
#: Also default value for
#: ``settings.NGINX_DOWNLOAD_MIDDLEWARE_WITH_BUFFERING``.
#:
#: See http://wiki.nginx.org/X-accel#X-Accel-Limit-Buffering
#:
#: Default value is None, which means "let Nginx choose", i.e. use Nginx
#: defaults or specific configuration.
#:
#: If set to ``False``, Nginx buffering is disabled.
#: If set to ``True``, Nginx buffering is enabled.
DEFAULT_WITH_BUFFERING = None
if not hasattr(settings, 'NGINX_DOWNLOAD_MIDDLEWARE_WITH_BUFFERING'):
setattr(settings, 'NGINX_DOWNLOAD_MIDDLEWARE_WITH_BUFFERING',
@ -22,19 +34,37 @@ if not hasattr(settings, 'NGINX_DOWNLOAD_MIDDLEWARE_WITH_BUFFERING'):
#: Default value for X-Accel-Limit-Rate header.
#: Also default value for ``settings.NGINX_DOWNLOAD_MIDDLEWARE_LIMIT_RATE``.
#:
#: See http://wiki.nginx.org/X-accel#X-Accel-Limit-Rate
#:
#: Default value is None, which means "let Nginx choose", i.e. use Nginx
#: defaults or specific configuration.
#:
#: If set to ``False``, Nginx limit rate is disabled.
#: Else, it indicates the limit rate in bytes.
DEFAULT_LIMIT_RATE = None
if not hasattr(settings, 'NGINX_DOWNLOAD_MIDDLEWARE_LIMIT_RATE'):
setattr(settings, 'NGINX_DOWNLOAD_MIDDLEWARE_LIMIT_RATE', DEFAULT_LIMIT_RATE)
#: Default value for X-Accel-Limit-Rate header.
#: Default value for X-Accel-Limit-Expires header.
#: Also default value for ``settings.NGINX_DOWNLOAD_MIDDLEWARE_EXPIRES``.
#:
#: See http://wiki.nginx.org/X-accel#X-Accel-Limit-Expires
#:
#: Default value is None, which means "let Nginx choose", i.e. use Nginx
#: defaults or specific configuration.
#:
#: If set to ``False``, Nginx buffering is disabled.
#: Else, it indicates the expiration delay, in seconds.
DEFAULT_EXPIRES = None
if not hasattr(settings, 'NGINX_DOWNLOAD_MIDDLEWARE_EXPIRES'):
setattr(settings, 'NGINX_DOWNLOAD_MIDDLEWARE_EXPIRES', DEFAULT_EXPIRES)
class XAccelRedirectResponse(HttpResponse):
"""Http response that delegate serving file to Nginx."""
"""Http response that delegates serving file to Nginx."""
def __init__(self, redirect_url, content_type, basename=None, expires=None,
with_buffering=None, limit_rate=None):
"""Return a HttpResponse with headers for Nginx X-Accel-Redirect."""
@ -56,7 +86,14 @@ class XAccelRedirectResponse(HttpResponse):
class BaseXAccelRedirectMiddleware(BaseDownloadMiddleware):
"""Looks like a middleware, but configurable."""
"""Looks like a middleware, but it is configurable.
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, media_root, media_url, expires=None,
with_buffering=None, limit_rate=None):
"""Constructor."""
@ -92,11 +129,7 @@ class BaseXAccelRedirectMiddleware(BaseDownloadMiddleware):
class XAccelRedirectMiddleware(BaseXAccelRedirectMiddleware):
"""Apply X-Accel-Redirect globally.
XAccelRedirectResponseHandler with django settings.
"""
"""Apply X-Accel-Redirect globally, via Django settings."""
def __init__(self):
"""Use Django settings as configuration."""
try:
@ -122,6 +155,6 @@ class XAccelRedirectMiddleware(BaseXAccelRedirectMiddleware):
#: Apply BaseXAccelRedirectMiddleware to ``view_func`` response.
#:
#: Proxies additional arguments (``*args``, ``**kwargs``) to
#: :py:meth:`django_downloadview.nginx.BaseXAccelRedirectMiddleware.__init__`:
#: ``expires``, ``with_buffering``, and ``limit_rate``.
#: :py:class:`BaseXAccelRedirectMiddleware` constructor (``expires``,
#: ``with_buffering``, and ``limit_rate``).
x_accel_redirect = DownloadDecorator(BaseXAccelRedirectMiddleware)

View file

@ -3,7 +3,12 @@ from django.http import HttpResponse
class DownloadResponse(HttpResponse):
"""File download response."""
"""File download response.
``content`` attribute is supposed to be a file object wrapper, which makes
this response "lazy".
"""
def __init__(self, content, content_type, content_length, basename,
status=200, content_encoding=None, expires=None,
filename=None, url=None):
@ -68,7 +73,7 @@ def is_download_response(response):
"""Return ``True`` if ``response`` is a download response.
Current implementation returns True if ``response`` is an instance of
:py:class:`django_downloadview.DownloadResponse`.
:py:class:`django_downloadview.response.DownloadResponse`.
"""
return isinstance(response, DownloadResponse)

View file

@ -0,0 +1,33 @@
#################################
Alternatives and related projects
#################################
This document presents other projects that provide similar or complementary
functionalities. It focuses on differences with django-downloadview.
*************************
Django's static file view
*************************
`Django has a builtin static file view`_. It can stream files. As explained in
Django documentation, it is designed for development purposes. For production,
static files'd better be served by some optimized server.
Django-downloadview can replace Django's builtin static file view:
* perform actions with Django when receiving download requests: check
permissions, generate files, gzip, logging, signals...
* delegate actual download to a reverse proxy for increased performance.
* disable optimization middlewares or decorators in development, if you want to
serve files with Django.
**********
References
**********
.. target-notes::
.. _`Django has a builtin static file view`:
https://docs.djangoproject.com/en/1.4/ref/contrib/staticfiles/#static-file-development-view

1
docs/about/authors.txt Normal file
View file

@ -0,0 +1 @@
.. include:: ../../AUTHORS

1
docs/about/changelog.txt Normal file
View file

@ -0,0 +1 @@
.. include:: ../../CHANGELOG

10
docs/about/index.txt Normal file
View file

@ -0,0 +1,10 @@
#########################
About django-downloadview
#########################
.. toctree::
alternatives
license
authors
changelog

1
docs/about/license.txt Normal file
View file

@ -0,0 +1 @@
.. include:: ../../LICENSE

9
docs/api/index.txt Normal file
View file

@ -0,0 +1,9 @@
###
API
###
Here is API documentation, generated from code.
.. toctree::
modules

1
docs/demo.txt Normal file
View file

@ -0,0 +1 @@
.. include:: ../demo/README

111
docs/dev.txt Normal file
View file

@ -0,0 +1,111 @@
###########################
Contributing to the project
###########################
This document provides guidelines for people who want to contribute to the
project.
**************
Create tickets
**************
Please use the `bugtracker`_ **before** starting some work:
* check if the bug or feature request has already been filed. It may have been
answered too!
* else create a new ticket.
* if you plan to contribute, tell us, so that we are given an opportunity to
give feedback as soon as possible.
* Then, in your commit messages, reference the ticket with some
``refs #TICKET-ID`` syntax.
***************
Fork and branch
***************
* Work in forks and branches.
* Prefix your branch with the ticket ID corresponding to the issue. As an
example, if you are working on ticket #23 which is about contribute
documentation, name your branch like ``23-contribute-doc``.
* If you work in a development branch and want to refresh it with changes from
master, please `rebase`_ or `merge-based rebase`_, i.e. don't merge master.
*******************************
Setup a development environment
*******************************
System requirements:
* `Python`_ version 2.6 or 2.7, available as ``python`` command.
.. note::
You may use `Virtualenv`_ to make sure the active ``python`` is the right
one.
* make and wget to use the provided :file:`Makefile`.
Execute:
.. code-block:: sh
git clone git@github.com:benoitbryon/django-downloadview.git
cd django-downloadview/
make develop
If you cannot execute the Makefile, read it and adapt the few commands it
contains to your needs.
************
The Makefile
************
A :file:`Makefile` is provided to ease development. Use it to:
* setup the development environment: ``make develop``
* update it, as an example, after a pull: ``make update``
* run tests: ``make test``
* build documentation: ``make documentation``
The :file:`Makefile` is intended to be a live reference for the development
environment.
*************
Documentation
*************
Follow `style guide for Sphinx-based documentations`_ when editing the
documentation.
**************
Test and build
**************
Use `the Makefile`_.
**********
References
**********
.. target-notes::
.. _`bugtracker`:
https://github.com/benoitbryon/django-downloadview/issues
.. _`rebase`: http://git-scm.com/book/en/Git-Branching-Rebasing
.. _`merge-based rebase`: http://tech.novapost.fr/psycho-rebasing-en.html
.. _`Python`: http://python.org
.. _`Virtualenv`: http://virtualenv.org
.. _`style guide for Sphinx-based documentations`:
http://documentation-style-guide-sphinx.readthedocs.org/

View file

@ -1,8 +1,3 @@
.. django-downloadview documentation master file, created by
sphinx-quickstart on Mon Aug 27 11:37:23 2012.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
.. include:: ../README
@ -13,8 +8,12 @@ Contents
.. toctree::
:maxdepth: 2
nginx
api/modules
demo
install
optimizations/index
api/index
about/index
dev
******************
@ -24,4 +23,3 @@ Indices and tables
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

1
docs/install.txt Normal file
View file

@ -0,0 +1 @@
.. include:: ../INSTALL

View file

@ -0,0 +1,51 @@
#############
Optimizations
#############
Some reverse proxies allow applications to delegate actual download to the
proxy:
* with Django, manage permissions, generate files...
* let the reverse proxy serve the file.
As a result, you get increased performance: reverse proxies are more efficient
than Django at serving static files.
.. toctree::
nginx
Currently, only `nginx's X-Accel`_ is supported, but `contributions are
welcome`_!
*****************
How does it work?
*****************
The feature is inspired by `Django's TemplateResponse`_: the download views
return some :py:class:`django_downloadview.response.DownloadResponse` instance.
Such a response doesn't contain file data.
By default, at the end of Django's request/response handling, Django is to
iterate over the ``content`` attribute of the response. In a
``DownloadResponse``, this ``content`` attribute is a file wrapper.
It means that decorators and middlewares are given an opportunity to capture
the ``DownloadResponse`` before the content of the file is loaded into memory
As an example, :py:class:`django_downloadview.nginx.XAccelRedirectMiddleware`
replaces ``DownloadResponse`` intance by some
:py:class:`django_downloadview.nginx.XAccelRedirectResponse`.
**********
References
**********
.. target-notes::
.. _`nginx's X-Accel`: http://wiki.nginx.org/X-accel
.. _`contributions are welcome`:
https://github.com/benoitbryon/django-downloadview/issues?labels=optimizations
.. _`Django's TemplateResponse`:
https://docs.djangoproject.com/en/1.4/ref/template-response/

View file

@ -1,6 +1,6 @@
###################
Nginx optimisations
###################
#####
Nginx
#####
If you serve Django behind Nginx, then you can delegate the file download
service to Nginx and get increased performance:
@ -15,19 +15,65 @@ See `Nginx X-accel documentation`_ for details.
Configure some download view
****************************
As an example, let's consider the following download view:
As an example, let's consider an application called "myapp".
* mapped on ``/document/<object-slug>/download``
* returns DownloadResponse corresponding to Document's model FileField
* Document storage root is :file:`/var/www/files/`
* FileField's ``upload_to`` is "document".
:file:`settings.py`:
Files live in ``/var/www/files/document/`` folder.
.. code-block:: python
INSTALLED_APPS = (
# ...
'myapp',
# ...
)
MYAPP_STORAGE_LOCATION = '/var/www/files/' # Could be MEDIA_ROOT for public
# files.
This application holds a ``Document`` model.
:file:`myapp/models.py`:
.. code-block:: python
from django.conf import settings
from django.core.files.storage import FileSystemStorage
from django.db import models
storage = FileSystemStorage(location=settings.MYAPP_STORAGE_LOCATION)
class Document(models.Model):
file = models.FileField(storage=storage, upload_to='document')
Notice the ``storage`` and ``upload_to`` parameters: files for ``Document``
model live in :file:`/var/www/files/document/` folder.
Then we configured a download view for this model, restricted to authenticated
users:
:file:`myapp/urls.py`:
.. code-block:: python
from django.conf.urls import url, url_patterns
from django.contrib.auth.decorators import login_required
from django_downloadview import ObjectDownloadView
from myapp.models import Document
download = login_required(ObjectDownloadView.as_view(model=Document))
url_patterns = ('',
url('^document/(?P<pk>[0-9]+/download/$', download, name='download'),
)
As is, Django is to serve the files, i.e. load chunks into memory and stream
them.
Nginx is much more efficient for the actual streaming.
Nginx is much more efficient for the actual streaming... Let's use it!
***************
@ -36,17 +82,46 @@ Configure Nginx
See `Nginx X-accel documentation`_ for details.
In this documentation, let's suppose we have something like this:
Here is what you could have in :file:`/etc/nginx/sites-available/default`:
.. code-block:: nginx
# Will serve /var/www/files/myfile.tar.gz
# When passed URI /protected_files/myfile.tar.gz
location /optimized-download {
internal;
alias /var/www/files;
charset utf-8;
# Django-powered service.
upstream frontend {
server 127.0.0.1:8000 fail_timeout=0;
}
server {
listen 80 default;
# File-download proxy.
#
# Will serve /var/www/files/myfile.tar.gz when passed URI
# like /optimized-download/myfile.tar.gz
#
# See http://wiki.nginx.org/X-accel
# and https://github.com/benoitbryon/django-downloadview
location /optimized-download {
internal;
# Location to files on disk.
# See Django's settings.NGINX_DOWNLOAD_MIDDLEWARE_MEDIA_ROOT
alias /var/www/files/;
}
# Proxy to Django-powered frontend.
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://frontend;
}
}
... where specific configuration is the ``location /optimized-download``
section.
.. note::
``/optimized-download`` is not available for the client, i.e. users
@ -74,12 +149,18 @@ Register it in your settings:
# ...
)
Optionally customize configuration (default is "use Nginx's defaults").
Setup the middleware:
.. code-block:: python
NGINX_DOWNLOAD_MIDDLEWARE_MEDIA_ROOT = MEDIA_ROOT
NGINX_DOWNLOAD_MIDDLEWARE_MEDIA_ROOT = MYAPP_STORAGE_LOCATION
NGINX_DOWNLOAD_MIDDLEWARE_MEDIA_URL = '/optimized-download'
Optionally fine-tune the middleware. Default values are ``None``, which means
"use Nginx's defaults".
.. code-block:: python
NGINX_DOWNLOAD_MIDDLEWARE_EXPIRES = False # Force no expiration.
NGINX_DOWNLOAD_MIDDLEWARE_WITH_BUFFERING = False # Force buffering off.
NGINX_DOWNLOAD_MIDDLEWARE_LIMIT_RATE = False # Force limit rate off.
@ -92,128 +173,26 @@ Local delegation, with x_accel_redirect decorator
If you want to delegate file downloads to Nginx on a per-view basis, then use
:py:func:`django_downloadview.nginx.x_accel_redirect` decorator.
In some urls.py:
Adapt :file:`myapp/urls.py`:
.. code-block:: python
# ... import Document and django.core.urls
from django_downloadview import ObjectDownloadView
from django_downloadview.nginx import x_accel_redirect
download = x_accel_redirect(ObjectDownloadView.as_view(model=Document),
media_root=settings.MEDIA_ROOT,
media_url='/optimized-download')
# ... URL patterns using ``download``
********************
Sample configuration
********************
In this sample configuration...
* we register files in some "myapp.models.Document" model
* store files in :file:`/var/www/private/` folder
* publish files at ``/download/<pk>/`` URL
* restrict access to authenticated users with the ``login_required`` decorator
* delegate file download to Nginx, via ``/private/`` internal URL.
Nginx
=====
:file:`/etc/nginx/sites-available/default`:
.. code-block:: nginx
charset utf-8;
# Django-powered service.
upstream frontend {
server 127.0.0.1:8000 fail_timeout=0;
}
server {
listen 80 default;
# File-download proxy.
# See http://wiki.nginx.org/X-accel
# and https://github.com/benoitbryon/django-downloadview
location /private/ {
internal;
# Location to files on disk.
# See Django's settings.NGINX_DOWNLOAD_MIDDLEWARE_MEDIA_ROOT
alias /var/www/private/;
}
# Proxy to Django-powered frontend.
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://frontend;
}
}
Django settings
===============
:file:`settings.py`:
.. code-block:: python
MYAPP_STORAGE_LOCATION = '/var/www/private/'
NGINX_DOWNLOAD_MIDDLEWARE_MEDIA_ROOT = MYAPP_STORAGE_LOCATION
MIDDLEWARE_CLASSES = (
# ...
'django_downloadview.nginx.XAccelRedirectMiddleware',
# ...
)
INSTALLED_APPS = (
# ...
'myapp',
# ...
)
In some model
=============
:file:`myapp/models.py`:
.. code-block:: python
from django.conf import settings
from django.db import models
from django.core.files.storage import FileSystemStorage
storage = FileSystemStorage(location=settings.MYAPP_STORAGE_LOCATION)
class Document(models.Model):
file = models.ImageField(storage=storage)
URL patterns
============
:file:`myapp/urls.py`:
.. code-block:: python
.. code-block:: diff
from django.conf.urls import url, url_patterns
from django.contrib.auth.decorators import login_required
from django_downloadview import ObjectDownloadView
+ from django_downloadview.nginx import x_accel_redirect
from myapp.models import Document
download = login_required(ObjectDownloadView.as_view(model=Document))
+ download = x_accel_redirect(download,
+ media_root=settings.MY_APP_STORAGE_LOCATION,
+ media_url='/optimized-download')
url_patterns = ('',
url('^download/(?P<pk>[0-9]+/$', download, name='download'),
url('^document/(?P<pk>[0-9]+/download/$', download, name='download'),
)

View file

@ -22,13 +22,14 @@ setup(name=NAME,
version=VERSION,
description='Generic download views for Django.',
long_description=README,
classifiers=['Development Status :: 1 - Planning',
classifiers=['Development Status :: 4 - Beta',
'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 2.6',
'Framework :: Django',
],
keywords='class-based view, generic view, download',
keywords='class-based view, generic view, download, file, FileField, ' \
'ImageField, nginx, x-accel, x-sendfile',
author='Benoit Bryon',
author_email='benoit@marmelune.net',
url='https://github.com/benoitbryon/%s' % NAME,