mirror of
https://github.com/jazzband/django-downloadview.git
synced 2026-03-16 22:40:25 +00:00
ObjectDownloadView now inherits from SingleObjectMixin and BaseDownloadView. Added example for setup_view() in documentation.
This commit is contained in:
parent
0371f84f26
commit
2c1ad3c730
15 changed files with 126 additions and 68 deletions
7
Makefile
7
Makefile
|
|
@ -7,6 +7,7 @@ DATA_DIR = $(ROOT_DIR)/var
|
|||
WGET = wget
|
||||
PYTHON = $(shell which python)
|
||||
PROJECT = $(shell $(PYTHON) -c "import setup; print setup.NAME")
|
||||
PACKAGE = $(shell $(PYTHON) -c "import setup; print setup.PACKAGES[0]")
|
||||
BUILDOUT_CFG = $(ROOT_DIR)/etc/buildout.cfg
|
||||
BUILDOUT_DIR = $(ROOT_DIR)/lib/buildout
|
||||
BUILDOUT_VERSION = 1.7.0
|
||||
|
|
@ -51,17 +52,17 @@ test: test-app test-demo test-documentation
|
|||
|
||||
|
||||
test-app:
|
||||
$(NOSE) -c $(ROOT_DIR)/etc/nose.cfg --with-coverage --cover-package=django_downloadview django_downloadview tests
|
||||
$(NOSE) -c $(ROOT_DIR)/etc/nose/base.cfg -c $(ROOT_DIR)/etc/nose/$(PACKAGE).cfg
|
||||
mv $(ROOT_DIR)/.coverage $(ROOT_DIR)/var/test/app.coverage
|
||||
|
||||
|
||||
test-demo:
|
||||
$(BIN_DIR)/demo test demo
|
||||
$(BIN_DIR)/demo test --nose-verbosity=2
|
||||
mv $(ROOT_DIR)/.coverage $(ROOT_DIR)/var/test/demo.coverage
|
||||
|
||||
|
||||
test-documentation:
|
||||
$(NOSE) -c $(ROOT_DIR)/etc/nose.cfg sphinxcontrib.testbuild.tests
|
||||
$(NOSE) -c $(ROOT_DIR)/etc/nose/base.cfg sphinxcontrib.testbuild.tests
|
||||
|
||||
|
||||
sphinx:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ demoproject_dir = dirname(abspath(__file__))
|
|||
demo_dir = dirname(demoproject_dir)
|
||||
root_dir = dirname(demo_dir)
|
||||
data_dir = join(root_dir, 'var')
|
||||
cfg_dir = join(root_dir, 'etc')
|
||||
|
||||
|
||||
# Mandatory settings.
|
||||
|
|
@ -76,21 +77,10 @@ DOWNLOADVIEW_MIDDLEWARES = (
|
|||
)
|
||||
|
||||
|
||||
# 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
|
||||
'--id-file=%s' % join(data_dir, 'test', 'noseids'),
|
||||
'--with-doctest',
|
||||
'--with-xunit',
|
||||
'--xunit-file=%s' % join(data_dir, 'test', 'nosetests.xml'),
|
||||
'--with-coverage',
|
||||
'--cover-erase',
|
||||
'--cover-package=django_downloadview',
|
||||
'--no-path-adjustment',
|
||||
'--all-modules',
|
||||
]
|
||||
nose_cfg_dir = join(cfg_dir, 'nose')
|
||||
NOSE_ARGS = ['--config={etc}/base.cfg'.format(etc=nose_cfg_dir),
|
||||
'--config={etc}/{package}.cfg'.format(etc=nose_cfg_dir,
|
||||
package=__package__)]
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
try:
|
||||
from unittest import mock
|
||||
except ImportError: # Python 2.x fallback.
|
||||
import mock
|
||||
import unittest
|
||||
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.urlresolvers import reverse
|
||||
import django.test
|
||||
|
||||
from django_downloadview import temporary_media_root, assert_download_response
|
||||
from django_downloadview import assert_download_response, temporary_media_root
|
||||
from django_downloadview import setup_view
|
||||
|
||||
from demoproject.storage.views import storage
|
||||
from demoproject.storage import views
|
||||
|
||||
|
||||
# Fixtures.
|
||||
|
|
@ -17,13 +15,13 @@ file_content = 'Hello world!\n'
|
|||
|
||||
|
||||
def setup_file(path):
|
||||
storage.save(path, ContentFile(file_content))
|
||||
views.storage.save(path, ContentFile(file_content))
|
||||
|
||||
|
||||
class StaticPathTestCase(django.test.TestCase):
|
||||
@temporary_media_root()
|
||||
def test_download_response(self):
|
||||
"""'static_path' streams file by path."""
|
||||
"""'storage:static_path' streams file by path."""
|
||||
setup_file('1.txt')
|
||||
url = reverse('storage:static_path', kwargs={'path': '1.txt'})
|
||||
response = self.client.get(url)
|
||||
|
|
@ -34,10 +32,20 @@ class StaticPathTestCase(django.test.TestCase):
|
|||
mime_type='text/plain')
|
||||
|
||||
|
||||
class DynamicPathTestCase(django.test.TestCase):
|
||||
class DynamicPathIntegrationTestCase(django.test.TestCase):
|
||||
"""Integration tests around ``storage:dynamic_path`` URL."""
|
||||
@temporary_media_root()
|
||||
def test_download_response(self):
|
||||
"""'dynamic_path' streams file by generated path."""
|
||||
"""'dynamic_path' streams file by generated path.
|
||||
|
||||
As we use ``self.client``, this test involves the whole Django stack,
|
||||
including settings, middlewares, decorators... So we need to setup a
|
||||
file, the storage, and an URL.
|
||||
|
||||
This test actually asserts the URL ``storage:dynamic_path`` streams a
|
||||
file in storage.
|
||||
|
||||
"""
|
||||
setup_file('1.TXT')
|
||||
url = reverse('storage:dynamic_path', kwargs={'path': '1.txt'})
|
||||
response = self.client.get(url)
|
||||
|
|
@ -46,3 +54,23 @@ class DynamicPathTestCase(django.test.TestCase):
|
|||
content=file_content,
|
||||
basename='1.TXT',
|
||||
mime_type='text/plain')
|
||||
|
||||
|
||||
class DynamicPathUnitTestCase(unittest.TestCase):
|
||||
"""Unit tests around ``views.DynamicStorageDownloadView``."""
|
||||
def test_get_path(self):
|
||||
"""DynamicStorageDownloadView.get_path() returns uppercase path.
|
||||
|
||||
Uses :func:`~django_downloadview.test.setup_view` to target only
|
||||
overriden methods.
|
||||
|
||||
This test does not involve URLconf, middlewares or decorators. It is
|
||||
fast. It has clear scope. It does not assert ``storage:dynamic_path``
|
||||
URL works. It targets only custom ``DynamicStorageDownloadView`` class.
|
||||
|
||||
"""
|
||||
view = setup_view(views.DynamicStorageDownloadView(),
|
||||
django.test.RequestFactory().get('/fake-url'),
|
||||
path='dummy path')
|
||||
path = view.get_path()
|
||||
self.assertEqual(path, 'DUMMY PATH')
|
||||
|
|
|
|||
|
|
@ -18,4 +18,5 @@ from django_downloadview.views import (PathDownloadView, # NoQA
|
|||
DownloadMixin)
|
||||
from django_downloadview.sendfile import sendfile # NoQA
|
||||
from django_downloadview.test import (assert_download_response, # NoQA
|
||||
setup_view,
|
||||
temporary_media_root)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,34 @@ from django.test.utils import override_settings
|
|||
from django_downloadview.middlewares import is_download_response
|
||||
|
||||
|
||||
def setup_view(view, request, *args, **kwargs):
|
||||
"""Mimic ``as_view()``, but returns view instance.
|
||||
|
||||
Use this function to get view instances on which you can run unit tests,
|
||||
by testing specific methods.
|
||||
|
||||
This is an early implementation of
|
||||
https://code.djangoproject.com/ticket/20456
|
||||
|
||||
``view``
|
||||
A view instance, such as ``TemplateView(template_name='dummy.html')``.
|
||||
Initialization arguments are the same you would pass to ``as_view()``.
|
||||
|
||||
``request``
|
||||
A request object, typically built with
|
||||
:class:`~django.test.client.RequestFactory`.
|
||||
|
||||
``args`` and ``kwargs``
|
||||
"URLconf" positional and keyword arguments, the same you would pass to
|
||||
:func:`~django.core.urlresolvers.reverse`.
|
||||
|
||||
"""
|
||||
view.request = request
|
||||
view.args = args
|
||||
view.kwargs = kwargs
|
||||
return view
|
||||
|
||||
|
||||
class temporary_media_root(override_settings):
|
||||
"""Temporarily override settings.MEDIA_ROOT with a temporary directory.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,2 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Unit tests."""
|
||||
|
||||
|
||||
def setup_view(view, request, *args, **kwargs):
|
||||
"""Mimic as_view() returned callable, but returns view instance.
|
||||
|
||||
``args`` and ``kwargs`` are the same you would pass to
|
||||
:func:`~django.core.urlresolvers.reverse`.
|
||||
|
||||
This is an early implementation of
|
||||
https://code.djangoproject.com/ticket/20456
|
||||
|
||||
"""
|
||||
view.request = request
|
||||
view.args = args
|
||||
view.kwargs = kwargs
|
||||
return view
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ class APITestCase(unittest.TestCase):
|
|||
'DownloadDispatcherMiddleware',
|
||||
# Testing:
|
||||
'assert_download_response',
|
||||
'setup_view',
|
||||
'temporary_media_root',
|
||||
# Utilities:
|
||||
'StringIteratorIO',
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ except ImportError:
|
|||
from django.http.response import HttpResponseNotModified
|
||||
import django.test
|
||||
|
||||
from django_downloadview.tests import setup_view
|
||||
from django_downloadview.test import setup_view
|
||||
from django_downloadview.views import base
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -66,19 +66,19 @@ class DownloadMixin(object):
|
|||
else:
|
||||
return was_modified_since(since, modification_time, size)
|
||||
|
||||
def not_modified_response(self, **response_kwargs):
|
||||
def not_modified_response(self, *response_args, **response_kwargs):
|
||||
"""Return :class:`django.http.HttpResponseNotModified` instance."""
|
||||
return HttpResponseNotModified(**response_kwargs)
|
||||
return HttpResponseNotModified(*response_args, **response_kwargs)
|
||||
|
||||
def download_response(self, **response_kwargs):
|
||||
def download_response(self, *response_args, **response_kwargs):
|
||||
"""Return :class:`~django_downloadview.response.DownloadResponse`."""
|
||||
response_kwargs.setdefault('file_instance', self.file_instance)
|
||||
response_kwargs.setdefault('attachment', self.attachment)
|
||||
response_kwargs.setdefault('basename', self.get_basename())
|
||||
response = self.response_class(**response_kwargs)
|
||||
response = self.response_class(*response_args, **response_kwargs)
|
||||
return response
|
||||
|
||||
def render_to_response(self, **response_kwargs):
|
||||
def render_to_response(self, *response_args, **response_kwargs):
|
||||
"""Return "download" response.
|
||||
|
||||
Respects the "HTTP_IF_MODIFIED_SINCE" header if any. In that case, uses
|
||||
|
|
@ -94,7 +94,7 @@ class DownloadMixin(object):
|
|||
if not self.was_modified_since(self.file_instance, since):
|
||||
return self.not_modified_response(**response_kwargs)
|
||||
# Return download response.
|
||||
return self.download_response(**response_kwargs)
|
||||
return self.download_response(*response_args, **response_kwargs)
|
||||
|
||||
|
||||
class BaseDownloadView(DownloadMixin, View):
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Stream files that live in models."""
|
||||
from django.views.generic.detail import BaseDetailView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
|
||||
from django_downloadview.views.base import DownloadMixin
|
||||
from django_downloadview.views.base import BaseDownloadView
|
||||
|
||||
|
||||
class ObjectDownloadView(DownloadMixin, BaseDetailView):
|
||||
class ObjectDownloadView(SingleObjectMixin, BaseDownloadView):
|
||||
"""Serve file fields from models.
|
||||
|
||||
This class extends BaseDetailView, so you can use its arguments to target
|
||||
|
|
@ -76,3 +76,7 @@ class ObjectDownloadView(DownloadMixin, BaseDetailView):
|
|||
if model_field:
|
||||
basename = getattr(self.object, model_field)
|
||||
return basename
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
return super(ObjectDownloadView, self).get(request, *args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -4,18 +4,12 @@ Write tests
|
|||
|
||||
`django_downloadview` embeds test utilities:
|
||||
|
||||
* :func:`~django_downloadview.test.assert_download_response`
|
||||
* :func:`~django_downloadview.test.temporary_media_root`
|
||||
* :func:`~django_downloadview.test.assert_download_response`
|
||||
* :func:`~django_downloadview.test.setup_view`
|
||||
* :func:`~django_downloadview.nginx.tests.assert_x_accel_redirect`
|
||||
|
||||
|
||||
************************
|
||||
assert_download_response
|
||||
************************
|
||||
|
||||
.. autofunction:: django_downloadview.test.assert_download_response
|
||||
|
||||
|
||||
********************
|
||||
temporary_media_root
|
||||
********************
|
||||
|
|
@ -23,11 +17,28 @@ temporary_media_root
|
|||
.. autofunction:: django_downloadview.test.temporary_media_root
|
||||
|
||||
|
||||
*******
|
||||
Example
|
||||
*******
|
||||
************************
|
||||
assert_download_response
|
||||
************************
|
||||
|
||||
Here are the tests related to :doc:`StorageDownloadView demo </views/storage>`:
|
||||
.. autofunction:: django_downloadview.test.assert_download_response
|
||||
|
||||
Examples, related to :doc:`StorageDownloadView demo </views/storage>`:
|
||||
|
||||
.. literalinclude:: /../demo/demoproject/storage/tests.py
|
||||
:language: python
|
||||
:lines: 3-7, 9-57
|
||||
|
||||
|
||||
**********
|
||||
setup_view
|
||||
**********
|
||||
|
||||
.. autofunction:: django_downloadview.test.setup_view
|
||||
|
||||
Example, related to :doc:`StorageDownloadView demo </views/storage>`:
|
||||
|
||||
.. literalinclude:: /../demo/demoproject/storage/tests.py
|
||||
:language: python
|
||||
:lines: 1-2, 8-12, 59-
|
||||
|
||||
|
|
|
|||
|
|
@ -70,12 +70,12 @@ collective.recipe.omelette = 0.16
|
|||
coverage = 3.6
|
||||
distribute = 0.6.34
|
||||
Django = 1.5
|
||||
django-nose = 1.1
|
||||
django-nose = 1.2
|
||||
docutils = 0.10
|
||||
evg.recipe.activate = 0.5
|
||||
Jinja2 = 2.6
|
||||
mock = 1.0.1
|
||||
nose = 1.2.1
|
||||
nose = 1.3.0
|
||||
Pygments = 1.6
|
||||
python-termstyle = 0.1.10
|
||||
rednose = 0.3
|
||||
|
|
|
|||
|
|
@ -5,3 +5,5 @@ with-doctest = True
|
|||
rednose = True
|
||||
no-path-adjustment = True
|
||||
all-modules = True
|
||||
cover-inclusive = True
|
||||
cover-tests = True
|
||||
4
etc/nose/demoproject.cfg
Normal file
4
etc/nose/demoproject.cfg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[nosetests]
|
||||
with-coverage = True
|
||||
cover-package = demoproject
|
||||
tests = demoproject
|
||||
4
etc/nose/django_downloadview.cfg
Normal file
4
etc/nose/django_downloadview.cfg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[nosetests]
|
||||
with-coverage = True
|
||||
cover-package = django_downloadview
|
||||
tests = django_downloadview,tests
|
||||
Loading…
Reference in a new issue