mirror of
https://github.com/jazzband/django-downloadview.git
synced 2026-03-16 22:40:25 +00:00
Reviewed documentation. Preparing first public release.
This commit is contained in:
parent
7738d1c0b6
commit
4f29852ddb
27 changed files with 535 additions and 197 deletions
7
AUTHORS
7
AUTHORS
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
25
INSTALL
|
|
@ -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>`.
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
recursive-include django_downloadview *
|
||||
global-exclude *.pyc .*.swp
|
||||
include *.txt
|
||||
include *.rst
|
||||
include AUTHORS CHANGELOG INSTALL LICENSE README VERSION
|
||||
|
|
|
|||
43
Makefile
43
Makefile
|
|
@ -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
5
README
|
|
@ -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
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.1
|
||||
1.0dev
|
||||
|
|
|
|||
11
buildout.cfg
11
buildout.cfg
|
|
@ -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
|
||||
|
|
|
|||
63
demo/README
63
demo/README
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
33
docs/about/alternatives.txt
Normal file
33
docs/about/alternatives.txt
Normal 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
1
docs/about/authors.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
.. include:: ../../AUTHORS
|
||||
1
docs/about/changelog.txt
Normal file
1
docs/about/changelog.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
.. include:: ../../CHANGELOG
|
||||
10
docs/about/index.txt
Normal file
10
docs/about/index.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#########################
|
||||
About django-downloadview
|
||||
#########################
|
||||
|
||||
.. toctree::
|
||||
|
||||
alternatives
|
||||
license
|
||||
authors
|
||||
changelog
|
||||
1
docs/about/license.txt
Normal file
1
docs/about/license.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
.. include:: ../../LICENSE
|
||||
9
docs/api/index.txt
Normal file
9
docs/api/index.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
###
|
||||
API
|
||||
###
|
||||
|
||||
Here is API documentation, generated from code.
|
||||
|
||||
.. toctree::
|
||||
|
||||
modules
|
||||
1
docs/demo.txt
Normal file
1
docs/demo.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
.. include:: ../demo/README
|
||||
111
docs/dev.txt
Normal file
111
docs/dev.txt
Normal 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/
|
||||
|
|
@ -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
1
docs/install.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
.. include:: ../INSTALL
|
||||
51
docs/optimizations/index.txt
Normal file
51
docs/optimizations/index.txt
Normal 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/
|
||||
|
|
@ -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'),
|
||||
)
|
||||
|
||||
|
||||
5
setup.py
5
setup.py
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue