mirror of
https://github.com/jazzband/django-downloadview.git
synced 2026-03-16 22:40:25 +00:00
Refs #23 - Implemented and tested PathDownloadView. This PathDownloadView is to replace an use case of former DownloadView.
This commit is contained in:
parent
b8d51c12fb
commit
cc3df73905
8 changed files with 148 additions and 41 deletions
|
|
@ -4,7 +4,7 @@ from os import listdir
|
|||
from os.path import abspath, dirname, join
|
||||
|
||||
from django.core.files import File
|
||||
from django.core.urlresolvers import reverse_lazy as reverse
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase
|
||||
|
||||
from django_downloadview.test import temporary_media_root
|
||||
|
|
@ -25,13 +25,8 @@ class DownloadTestCase(TestCase):
|
|||
for f in listdir(fixtures_dir):
|
||||
self.files[f] = abspath(join(fixtures_dir, f))
|
||||
|
||||
|
||||
class DownloadViewTestCase(DownloadTestCase):
|
||||
"""Test generic DownloadView."""
|
||||
def test_download_hello_world(self):
|
||||
"""download_hello_world view returns hello-world.txt as attachement."""
|
||||
download_url = reverse('download_hello_world')
|
||||
response = self.client.get(download_url)
|
||||
def assertDownloadHelloWorld(self, response, is_attachment=True):
|
||||
"""Assert response is 'hello-world.txt' download."""
|
||||
self.assertEquals(response.status_code, 200)
|
||||
self.assertEquals(response['Content-Type'],
|
||||
'text/plain; charset=utf-8')
|
||||
|
|
@ -42,21 +37,32 @@ class DownloadViewTestCase(DownloadTestCase):
|
|||
response.content)
|
||||
|
||||
|
||||
class PathDownloadViewTestCase(DownloadTestCase):
|
||||
"""Test "hello_world" view."""
|
||||
def test_download_hello_world(self):
|
||||
"""hello_world view returns hello-world.txt as attachement."""
|
||||
download_url = reverse('hello_world')
|
||||
response = self.client.get(download_url)
|
||||
self.assertDownloadHelloWorld(response)
|
||||
|
||||
|
||||
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."""
|
||||
download_url = reverse('fixture_from_path', args=['hello-world.txt'])
|
||||
response = self.client.get(download_url)
|
||||
self.assertDownloadHelloWorld(response)
|
||||
|
||||
|
||||
class ObjectDownloadViewTestCase(DownloadTestCase):
|
||||
"""Test generic ObjectDownloadView."""
|
||||
@temporary_media_root()
|
||||
def test_download_hello_world(self):
|
||||
"""'download_document' view returns hello-world.txt as attachement."""
|
||||
slug = 'hello-world'
|
||||
download_url = reverse('download_document', kwargs={'slug': slug})
|
||||
download_url = reverse('document', kwargs={'slug': slug})
|
||||
Document.objects.create(slug=slug,
|
||||
file=File(open(self.files['hello-world.txt'])))
|
||||
response = self.client.get(download_url)
|
||||
self.assertEquals(response.status_code, 200)
|
||||
self.assertEquals(response['Content-Type'],
|
||||
'text/plain; charset=utf-8')
|
||||
self.assertFalse('ContentEncoding' in response)
|
||||
self.assertEquals(response['Content-Disposition'],
|
||||
'attachment; filename=hello-world.txt')
|
||||
self.assertEqual(open(self.files['hello-world.txt']).read(),
|
||||
response.content)
|
||||
self.assertDownloadHelloWorld(response)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,19 @@
|
|||
# coding=utf8
|
||||
"""URL mapping."""
|
||||
from django.conf.urls import patterns, include, url
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
|
||||
urlpatterns = patterns('demoproject.download.views',
|
||||
url(r'^hello-world\.txt$', 'download_hello_world',
|
||||
name='download_hello_world'),
|
||||
url(r'^document/(?P<slug>[a-zA-Z0-9_-]+)/$', 'download_document',
|
||||
name='download_document'),
|
||||
urlpatterns = patterns(
|
||||
'demoproject.download.views',
|
||||
# Path-based downloads.
|
||||
url(r'^hello-world\.txt$',
|
||||
'download_hello_world',
|
||||
name='hello_world'),
|
||||
url(r'^path/(?P<path>[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<slug>[a-zA-Z0-9_-]+)/$',
|
||||
'download_document',
|
||||
name='document'),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,16 +1,54 @@
|
|||
# coding=utf8
|
||||
"""Demo download views."""
|
||||
from os.path import abspath, dirname, join
|
||||
|
||||
from django_downloadview import DownloadView, ObjectDownloadView
|
||||
from django_downloadview.views import ObjectDownloadView, PathDownloadView
|
||||
|
||||
from demoproject.download.models import Document
|
||||
|
||||
|
||||
# Some initializations.
|
||||
|
||||
app_dir = dirname(abspath(__file__))
|
||||
"""Directory containing code of :py:module:`demoproject.download.views`."""
|
||||
|
||||
fixtures_dir = join(app_dir, 'fixtures')
|
||||
hello_world_file = join(fixtures_dir, 'hello-world.txt')
|
||||
"""Directory containing files fixtures."""
|
||||
|
||||
hello_world_path = join(fixtures_dir, 'hello-world.txt')
|
||||
"""Path to a text file that says 'Hello world!'."""
|
||||
|
||||
|
||||
download_hello_world = DownloadView.as_view(filename=hello_world_file,
|
||||
storage=None)
|
||||
# Here are the views.
|
||||
|
||||
download_hello_world = PathDownloadView.as_view(path=hello_world_path)
|
||||
"""Direct download of one file, based on an absolute path.
|
||||
|
||||
You could use this example as a shortcut, inside other views.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class CustomPathDownloadView(PathDownloadView):
|
||||
"""Example of customized PathDownloadView."""
|
||||
def get_path(self):
|
||||
"""Convert relative path (provided in URL) into absolute path.
|
||||
|
||||
Notice that this particularly simple use case is covered by
|
||||
:py:class:`django_downloadview.views.StorageDownloadView`.
|
||||
|
||||
.. warning::
|
||||
|
||||
If you are doing such things, make the path secure! Prevent users
|
||||
to download files anywhere in the filesystem.
|
||||
|
||||
"""
|
||||
path = super(CustomPathDownloadView, self).get_path()
|
||||
return join(fixtures_dir, path)
|
||||
|
||||
download_fixture_from_path = CustomPathDownloadView.as_view()
|
||||
"""Pre-configured :py:class:`CustomPathDownloadView`."""
|
||||
|
||||
|
||||
download_document = ObjectDownloadView.as_view(model=Document)
|
||||
"""Pre-configured download view for :py:class:`Document` model."""
|
||||
|
|
|
|||
|
|
@ -65,14 +65,14 @@ MIDDLEWARE_CLASSES = [
|
|||
#NGINX_DOWNLOAD_MIDDLEWARE_MEDIA_URL = "/proxied-download"
|
||||
|
||||
|
||||
# Development configuratio.
|
||||
# Development configuration.
|
||||
DEBUG = True
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
|
||||
NOSE_ARGS = ['--verbose',
|
||||
'--nocapture',
|
||||
'--rednose',
|
||||
'--with-id', # allows --failed which only reruns failed tests
|
||||
'--with-id', # allows --failed which only reruns failed tests
|
||||
'--id-file=%s' % join(data_dir, 'test', 'noseids'),
|
||||
'--with-doctest',
|
||||
'--with-xunit',
|
||||
|
|
@ -81,4 +81,5 @@ NOSE_ARGS = ['--verbose',
|
|||
'--cover-erase',
|
||||
'--cover-package=django_downloadview',
|
||||
'--no-path-adjustment',
|
||||
]
|
||||
'--all-modules',
|
||||
]
|
||||
|
|
|
|||
|
|
@ -7,9 +7,18 @@
|
|||
<h1>Welcome to django-downloadview demo!</h1>
|
||||
<p>Here are some demo links. Browse the code to see how they are implemented</p>
|
||||
<ul>
|
||||
<li><a href="{% url 'download_hello_world' %}">DownloadView</a></li>
|
||||
<li><a href="{% url 'download_document' 'hello-world' %}">ObjectDownloadView</a></li>
|
||||
<li><a href="{% url 'download_document_nginx' 'hello-world' %}">ObjectDownloadView decorated with nginx X-Accel-Redirect</a> (better if served behind nginx)</li>
|
||||
<li><a href="{% url 'hello_world' %}">
|
||||
Direct download to one file using PathDownloadView.
|
||||
</a></li>
|
||||
<li><a href="{% url 'fixture_from_path' 'hello-world.txt' %}">
|
||||
Download files using PathDownloadView and relative path in URL.
|
||||
</a></li>
|
||||
<li><a href="{% url 'document' 'hello-world' %}">
|
||||
ObjectDownloadView
|
||||
</a></li>
|
||||
<li><a href="{% url 'download_document_nginx' 'hello-world' %}">
|
||||
ObjectDownloadView decorated with nginx X-Accel-Redirect
|
||||
</a> to be served behind Nginx</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
13
demo/demoproject/tests.py
Normal file
13
demo/demoproject/tests.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# coding=utf8
|
||||
"""Test suite for demoproject.download."""
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class HomeViewTestCase(TestCase):
|
||||
"""Test homepage."""
|
||||
def test_get(self):
|
||||
"""Homepage returns HTTP 200."""
|
||||
home_url = reverse('home')
|
||||
response = self.client.get(home_url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
@ -5,11 +5,12 @@ from django.views.generic import TemplateView
|
|||
home = TemplateView.as_view(template_name='home.html')
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
# Standard download views.
|
||||
url(r'^download/', include('demoproject.download.urls')),
|
||||
# Nginx optimizations.
|
||||
url(r'^nginx/', include('demoproject.nginx.urls')),
|
||||
# An informative page.
|
||||
# An informative homepage.
|
||||
url(r'', home, name='home')
|
||||
)
|
||||
|
|
|
|||
|
|
@ -68,18 +68,48 @@ class BaseDownloadView(DownloadMixin, View):
|
|||
return self.render_to_response()
|
||||
|
||||
|
||||
class PathDownloadView(BaseDownloadView):
|
||||
"""Serve a file using filename."""
|
||||
path = None
|
||||
"""Server-side name (including path) of the file to serve.
|
||||
|
||||
Filename is supposed to be an absolute filename of a file located on the
|
||||
local filesystem.
|
||||
|
||||
"""
|
||||
|
||||
path_url_kwarg = 'path'
|
||||
"""Name of the URL argument that contains path."""
|
||||
|
||||
def get_path(self):
|
||||
"""Return actual path of the file to serve.
|
||||
|
||||
Default implementation simply returns view's :py:attr:`path`.
|
||||
|
||||
Override this method if you want custom implementation.
|
||||
As an example, :py:attr:`path` could be relative and your custom
|
||||
:py:meth:`get_path` implementation makes it absolute.
|
||||
|
||||
"""
|
||||
return self.kwargs.get(self.path_url_kwarg, self.path)
|
||||
|
||||
def get_file(self):
|
||||
"""Use path to return wrapper around file to serve."""
|
||||
return File(open(self.get_path()))
|
||||
|
||||
|
||||
class StorageDownloadView():
|
||||
"""Download a file from storage and filename."""
|
||||
"""Serve a file using storage and filename."""
|
||||
storage = DefaultStorage()
|
||||
path = None
|
||||
|
||||
|
||||
class SimpleDownloadView():
|
||||
"""Download a file from filename."""
|
||||
path = None
|
||||
|
||||
|
||||
class VirtualDownloadView():
|
||||
"""Serve not-on-disk or generated-on-the-fly file.
|
||||
|
||||
Use this class to serve :py:class:`StringIO` files.
|
||||
|
||||
"""
|
||||
file_obj = None
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue