From e94054bcba3e66854a73bf613fc8747e9a52ef9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Bryon?= Date: Wed, 6 Feb 2013 18:59:22 +0100 Subject: [PATCH] Refs #23 - Implemented and tested StorageDownloadView. This StorageDownloadView is to replace an use case of former DownloadView. --- CHANGELOG | 7 ++++++- README | 5 +++-- demo/demoproject/download/tests.py | 12 +++++++++++- demo/demoproject/download/urls.py | 12 ++++++++---- demo/demoproject/download/views.py | 13 ++++++++++++- demo/demoproject/templates/home.html | 3 +++ django_downloadview/views.py | 21 +++++++++++++++++++-- docs/views.txt | 11 +++++++++++ 8 files changed, 73 insertions(+), 11 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index bc8f47b..c4f962d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,7 +4,12 @@ Changelog 1.1 (unreleased) ---------------- -- Nothing changed yet. +**Backward incompatible changes.** + +- Download views and response now use file wrappers. Most logic around file + attributes, formerly in views, moved to wrappers. +- Replaced DownloadView by PathDownloadView and StorageDownloadView. Use the + right one depending on the use case. 1.0 (2012-12-04) diff --git a/README b/README index 603ab08..260271b 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ ################### -Django-DownloadView +django-downloadview ################### Django-DownloadView provides (class-based) generic download views for Django. @@ -22,8 +22,9 @@ Example, in some urls.py: Several views are provided to cover frequent use cases: -* ``PathDownloadView`` when you have an absolute filename. * ``ObjectDownloadView`` when you have a model with a file field. +* ``StorageDownloadView`` when you manage files in a storage. +* ``PathDownloadView`` when you have an absolute filename on local filesystem. See :doc:`views` for details. diff --git a/demo/demoproject/download/tests.py b/demo/demoproject/download/tests.py index 246ccf7..4cd535c 100644 --- a/demo/demoproject/download/tests.py +++ b/demo/demoproject/download/tests.py @@ -49,12 +49,22 @@ def test_download_hello_world(self): class CustomPathDownloadViewTestCase(DownloadTestCase): """Test "fixture_from_path" view.""" def test_download_hello_world(self): - """fixture_from_path view can return hello-world.txt as attachement.""" + """fixture_from_path view returns hello-world.txt as attachement.""" download_url = reverse('fixture_from_path', args=['hello-world.txt']) response = self.client.get(download_url) self.assertDownloadHelloWorld(response) +class StorageDownloadViewTestCase(DownloadTestCase): + """Test "fixture_from_storage" view.""" + def test_download_hello_world(self): + """fixture_from_storage view returns hello-world.txt as attachement.""" + download_url = reverse('fixture_from_storage', + args=['hello-world.txt']) + response = self.client.get(download_url) + self.assertDownloadHelloWorld(response) + + class ObjectDownloadViewTestCase(DownloadTestCase): """Test generic ObjectDownloadView.""" @temporary_media_root() diff --git a/demo/demoproject/download/urls.py b/demo/demoproject/download/urls.py index 234d974..c1f37e9 100644 --- a/demo/demoproject/download/urls.py +++ b/demo/demoproject/download/urls.py @@ -5,6 +5,14 @@ urlpatterns = patterns( 'demoproject.download.views', + # Model-based downloads. + url(r'^document/(?P[a-zA-Z0-9_-]+)/$', + 'download_document', + name='document'), + # Storage-based downloads. + url(r'^storage/(?P[a-zA-Z0-9_-]+\.[a-zA-Z0-9]{1,4})$', + 'download_fixture_from_storage', + name='fixture_from_storage'), # Path-based downloads. url(r'^hello-world\.txt$', 'download_hello_world', @@ -12,8 +20,4 @@ url(r'^path/(?P[a-zA-Z0-9_-]+\.[a-zA-Z0-9]{1,4})$', 'download_fixture_from_path', name='fixture_from_path'), - # Model-based downloads. - url(r'^document/(?P[a-zA-Z0-9_-]+)/$', - 'download_document', - name='document'), ) diff --git a/demo/demoproject/download/views.py b/demo/demoproject/download/views.py index 44e3315..a073aa6 100644 --- a/demo/demoproject/download/views.py +++ b/demo/demoproject/download/views.py @@ -2,7 +2,10 @@ """Demo download views.""" from os.path import abspath, dirname, join -from django_downloadview.views import ObjectDownloadView, PathDownloadView +from django.core.files.storage import FileSystemStorage + +from django_downloadview.views import (ObjectDownloadView, PathDownloadView, + StorageDownloadView) from demoproject.download.models import Document @@ -18,6 +21,9 @@ hello_world_path = join(fixtures_dir, 'hello-world.txt') """Path to a text file that says 'Hello world!'.""" +fixtures_storage = FileSystemStorage(location=fixtures_dir) +"""Storage for fixtures.""" + # Here are the views. @@ -50,5 +56,10 @@ def get_path(self): """Pre-configured :py:class:`CustomPathDownloadView`.""" +download_fixture_from_storage = StorageDownloadView.as_view( + storage=fixtures_storage) +"""Pre-configured view using a storage.""" + + download_document = ObjectDownloadView.as_view(model=Document) """Pre-configured download view for :py:class:`Document` model.""" diff --git a/demo/demoproject/templates/home.html b/demo/demoproject/templates/home.html index 4e499d6..556d3e9 100644 --- a/demo/demoproject/templates/home.html +++ b/demo/demoproject/templates/home.html @@ -13,6 +13,9 @@

Welcome to django-downloadview demo!

  • Download files using PathDownloadView and relative path in URL.
  • +
  • + Download files using StorageDownloadView and path in URL. +
  • ObjectDownloadView
  • diff --git a/django_downloadview/views.py b/django_downloadview/views.py index 7baa51b..4fb3428 100644 --- a/django_downloadview/views.py +++ b/django_downloadview/views.py @@ -98,10 +98,27 @@ def get_file(self): return File(open(self.get_path())) -class StorageDownloadView(): +class StorageDownloadView(PathDownloadView): """Serve a file using storage and filename.""" storage = DefaultStorage() - path = None + """Storage the file to serve belongs to.""" + + path = None # Override docstring. + """Path to the file to serve relative to storage.""" + + def get_path(self): + """Return path of the file to serve, relative to storage. + + Default implementation simply returns view's :py:attr:`path`. + + Override this method if you want custom implementation. + + """ + return super(StorageDownloadView, self).get_path() + + def get_file(self): + """Use path and storage to return wrapper around file to serve.""" + return self.storage.open(self.get_path()) class VirtualDownloadView(): diff --git a/docs/views.txt b/docs/views.txt index 37429dd..5330c43 100644 --- a/docs/views.txt +++ b/docs/views.txt @@ -23,6 +23,17 @@ Two main use cases: override :py:meth:`django_downloadview.views.PathDownloadView:get_path`. +******************* +StorageDownloadView +******************* + +The :py:class:`django_downloadview.views.StorageDownloadView` class-based view +allows you to **serve files given a storage and a path**. + +Use this view when you manage files in a storage (which is a good practice), +unrelated to a model. + + ****************** ObjectDownloadView ******************