mirror of
https://github.com/jazzband/django-downloadview.git
synced 2026-05-26 16:04:09 +00:00
Improved documentation around 'DownloadMixin.attachment': how to serve files inline. Closes #80.
This commit is contained in:
commit
8bfbbb36fc
14 changed files with 145 additions and 38 deletions
|
|
@ -10,6 +10,10 @@ future releases, check `milestones`_ and :doc:`/about/vision`.
|
|||
|
||||
- Feature #46: introduced support for Python>=3.3.
|
||||
|
||||
- Feature #80: added documentation about "how to serve a file inline VS how to
|
||||
serve a file as attachment". Improved documentation of views' base options
|
||||
inherited from ``DownloadMixin``.
|
||||
|
||||
- Feature #74: the Makefile in project's repository no longer creates a
|
||||
virtualenv. Developers setup the environment as they like, i.e. using
|
||||
virtualenv, virtualenvwrapper or whatever. Tests are run with tox.
|
||||
|
|
|
|||
|
|
@ -68,3 +68,17 @@ class DeserializedBasenameTestCase(django.test.TestCase):
|
|||
content=file_content,
|
||||
basename=basename,
|
||||
mime_type='text/plain')
|
||||
|
||||
|
||||
class InlineFileTestCase(django.test.TestCase):
|
||||
@temporary_media_root()
|
||||
def test_download_response(self):
|
||||
"'inline_file_view' streams Document.file inline."
|
||||
setup_document()
|
||||
url = reverse('object:inline_file', kwargs={'slug': slug})
|
||||
response = self.client.get(url)
|
||||
assert_download_response(self,
|
||||
response,
|
||||
content=file_content,
|
||||
mime_type='text/plain',
|
||||
attachment=False)
|
||||
|
|
|
|||
|
|
@ -14,4 +14,7 @@ urlpatterns = patterns(
|
|||
url(r'^deserialized_basename/(?P<slug>[a-zA-Z0-9_-]+)/$',
|
||||
views.deserialized_basename_view,
|
||||
name='deserialized_basename'),
|
||||
url(r'^inline-file/(?P<slug>[a-zA-Z0-9_-]+)/$',
|
||||
views.inline_file_view,
|
||||
name='inline_file'),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,3 +16,8 @@ another_file_view = ObjectDownloadView.as_view(
|
|||
deserialized_basename_view = ObjectDownloadView.as_view(
|
||||
model=Document,
|
||||
basename_field='basename')
|
||||
|
||||
#: Serve ``file`` attribute of ``Document`` model, inline (not as attachment).
|
||||
inline_file_view = ObjectDownloadView.as_view(
|
||||
model=Document,
|
||||
attachment=False)
|
||||
|
|
|
|||
|
|
@ -91,39 +91,7 @@ class DownloadResponse(StreamingHttpResponse):
|
|||
where :attr:`~django.http.StreamingHttpResponse.streaming_content` is a
|
||||
file wrapper.
|
||||
|
||||
Constructor differs a bit from :class:`~django.http.response.HttpResponse`:
|
||||
|
||||
``file_instance``
|
||||
A :doc:`file wrapper instance </files>`, such as
|
||||
:class:`~django.core.files.base.File`.
|
||||
|
||||
``attachement``
|
||||
Boolean. Whether to return the file as attachment or not.
|
||||
Affects ``Content-Disposition`` header.
|
||||
|
||||
``basename``
|
||||
Unicode. Client-side name of the file to stream.
|
||||
Only used if ``attachment`` is ``True``.
|
||||
Affects ``Content-Disposition`` header.
|
||||
|
||||
``status``
|
||||
HTTP status code.
|
||||
|
||||
``content_type``
|
||||
Value for ``Content-Type`` header.
|
||||
If ``None``, then mime-type and encoding will be populated by the
|
||||
response (default implementation uses mimetypes, based on file
|
||||
name).
|
||||
|
||||
``file_mimetype``
|
||||
Value for file's mimetype. If ``None`` (the default), then the file's
|
||||
mimetype will be guessed via Python's :mod:`mimetypes`. See
|
||||
:meth:`get_mime_type`.
|
||||
|
||||
``file_encoding``
|
||||
Value for file's encoding. If ``None`` (the default), then the file's
|
||||
encoding will be guessed via Python's :mod:`mimetypes`. See
|
||||
:meth:`get_encoding`.
|
||||
Constructor differs a bit from :class:`~django.http.response.HttpResponse`.
|
||||
|
||||
Here are some highlights to understand internal mechanisms and motivations:
|
||||
|
||||
|
|
@ -152,17 +120,42 @@ class DownloadResponse(StreamingHttpResponse):
|
|||
def __init__(self, file_instance, attachment=True, basename=None,
|
||||
status=200, content_type=None, file_mimetype=None,
|
||||
file_encoding=None):
|
||||
"""Constructor."""
|
||||
"""Constructor.
|
||||
|
||||
:param content_type: Value for ``Content-Type`` header.
|
||||
If ``None``, then mime-type and encoding will be
|
||||
populated by the response (default implementation
|
||||
uses :mod:`mimetypes`, based on file name).
|
||||
|
||||
"""
|
||||
#: A :doc:`file wrapper instance </files>`, such as
|
||||
#: :class:`~django.core.files.base.File`.
|
||||
self.file = file_instance
|
||||
super(DownloadResponse, self).__init__(streaming_content=self.file,
|
||||
status=status,
|
||||
content_type=content_type)
|
||||
|
||||
#: Client-side name of the file to stream.
|
||||
#: Only used if ``attachment`` is ``True``.
|
||||
#: Affects ``Content-Disposition`` header.
|
||||
self.basename = basename
|
||||
|
||||
#: Whether to return the file as attachment or not.
|
||||
#: Affects ``Content-Disposition`` header.
|
||||
self.attachment = attachment
|
||||
if not content_type:
|
||||
del self['Content-Type'] # Will be set later.
|
||||
|
||||
#: Value for file's mimetype.
|
||||
#: If ``None`` (the default), then the file's mimetype will be guessed
|
||||
#: via Python's :mod:`mimetypes`. See :meth:`get_mime_type`.
|
||||
self.file_mimetype = file_mimetype
|
||||
|
||||
#: Value for file's encoding. If ``None`` (the default), then the
|
||||
#: file's encoding will be guessed via Python's :mod:`mimetypes`. See
|
||||
#: :meth:`get_encoding`.
|
||||
self.file_encoding = file_encoding
|
||||
|
||||
# Apply default headers.
|
||||
for header, value in self.default_headers.items():
|
||||
if not header in self:
|
||||
|
|
|
|||
|
|
@ -144,8 +144,13 @@ class DownloadResponseValidator(object):
|
|||
value)
|
||||
|
||||
def assert_attachment(self, test_case, response, value):
|
||||
test_case.assertEqual('attachment;' in response['Content-Disposition'],
|
||||
value)
|
||||
if value:
|
||||
test_case.assertTrue(
|
||||
'attachment;' in response['Content-Disposition'])
|
||||
else:
|
||||
test_case.assertTrue(
|
||||
'Content-Disposition' not in response
|
||||
or 'attachment;' not in response['Content-Disposition'])
|
||||
|
||||
|
||||
def assert_download_response(test_case, response, **assertions):
|
||||
|
|
|
|||
|
|
@ -28,6 +28,21 @@ class DownloadMixin(object):
|
|||
response_class = DownloadResponse
|
||||
|
||||
#: Whether to return the response as attachment or not.
|
||||
#:
|
||||
#: When ``True`` (the default), the view returns file "as attachment",
|
||||
#: which usually triggers a "Save the file as ..." prompt.
|
||||
#:
|
||||
#: When ``False``, the view returns file "inline", as if it was an element
|
||||
#: of the current page.
|
||||
#:
|
||||
#: .. note::
|
||||
#:
|
||||
#: The actual behaviour client-side depends on the browser and its
|
||||
#: configuration.
|
||||
#:
|
||||
#: In fact, affects the "Content-Disposition" header via :attr:`response's
|
||||
#: attachment attribute
|
||||
#: <django_downloadview.response.DownloadResponse.attachment>`.
|
||||
attachment = True
|
||||
|
||||
#: Client-side filename, if only file is returned as attachment.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build -W # Turn warnings into errors.
|
||||
SPHINXBUILD = sphinx-build # Turn warnings into errors.
|
||||
PAPER =
|
||||
BUILDDIR = ../var/docs
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ Make your own view
|
|||
|
||||
.. currentmodule:: django_downloadview.views.base
|
||||
|
||||
|
||||
*************
|
||||
DownloadMixin
|
||||
*************
|
||||
|
|
@ -41,6 +42,22 @@ The only thing it does is to implement
|
|||
:member-order: bysource
|
||||
|
||||
|
||||
***********************************************
|
||||
Serving a file inline rather than as attachment
|
||||
***********************************************
|
||||
|
||||
Use :attr:`~DownloadMixin.attachment` to make a view serve a file inline rather
|
||||
than as attachment, i.e. to display the file as if it was an internal part of a
|
||||
page rather than triggering "Save file as..." prompt.
|
||||
|
||||
See details in :attr:`attachment API documentation
|
||||
<~DownloadMixin.attachment>`.
|
||||
|
||||
.. literalinclude:: /../demo/demoproject/object/views.py
|
||||
:language: python
|
||||
:lines: 1-5, 20-23
|
||||
|
||||
|
||||
************************************
|
||||
Handling http not modified responses
|
||||
************************************
|
||||
|
|
|
|||
|
|
@ -26,6 +26,16 @@ Setup a view to stream files given URL:
|
|||
:language: python
|
||||
|
||||
|
||||
************
|
||||
Base options
|
||||
************
|
||||
|
||||
:class:`HTTPDownloadView` inherits from
|
||||
:class:`~django_downloadview.views.base.DownloadMixin`, which has various
|
||||
options such as :attr:`~django_downloadview.views.base.DownloadMixin.basename`
|
||||
or :attr:`~django_downloadview.views.base.DownloadMixin.attachment`.
|
||||
|
||||
|
||||
*************
|
||||
API reference
|
||||
*************
|
||||
|
|
|
|||
|
|
@ -38,7 +38,17 @@ Setup a view to stream the ``file`` attribute:
|
|||
|
||||
.. literalinclude:: /../demo/demoproject/object/urls.py
|
||||
:language: python
|
||||
:lines: 1-7, 8-10, 17
|
||||
:lines: 1-7, 8-10, 20
|
||||
|
||||
|
||||
************
|
||||
Base options
|
||||
************
|
||||
|
||||
:class:`ObjectDownloadView` inherits from
|
||||
:class:`~django_downloadview.views.base.DownloadMixin`, which has various
|
||||
options such as :attr:`~django_downloadview.views.base.DownloadMixin.basename`
|
||||
or :attr:`~django_downloadview.views.base.DownloadMixin.attachment`.
|
||||
|
||||
|
||||
***************************
|
||||
|
|
@ -61,6 +71,7 @@ Then here is the code to serve "another_file" instead of the default "file":
|
|||
:language: python
|
||||
:lines: 1-5, 10-12
|
||||
|
||||
|
||||
**********************************
|
||||
Mapping file attributes to model's
|
||||
**********************************
|
||||
|
|
|
|||
|
|
@ -28,6 +28,16 @@ Setup a view to stream files given path:
|
|||
:emphasize-lines: 14
|
||||
|
||||
|
||||
************
|
||||
Base options
|
||||
************
|
||||
|
||||
:class:`PathDownloadView` inherits from
|
||||
:class:`~django_downloadview.views.base.DownloadMixin`, which has various
|
||||
options such as :attr:`~django_downloadview.views.base.DownloadMixin.basename`
|
||||
or :attr:`~django_downloadview.views.base.DownloadMixin.attachment`.
|
||||
|
||||
|
||||
**************************
|
||||
Computing path dynamically
|
||||
**************************
|
||||
|
|
|
|||
|
|
@ -34,6 +34,16 @@ via URLconfs:
|
|||
:lines: 1-7, 8-10, 14
|
||||
|
||||
|
||||
************
|
||||
Base options
|
||||
************
|
||||
|
||||
:class:`StorageDownloadView` inherits from
|
||||
:class:`~django_downloadview.views.base.DownloadMixin`, which has various
|
||||
options such as :attr:`~django_downloadview.views.base.DownloadMixin.basename`
|
||||
or :attr:`~django_downloadview.views.base.DownloadMixin.attachment`.
|
||||
|
||||
|
||||
**************************
|
||||
Computing path dynamically
|
||||
**************************
|
||||
|
|
|
|||
|
|
@ -18,6 +18,16 @@ it returns a suitable file wrapper...
|
|||
exited.
|
||||
|
||||
|
||||
************
|
||||
Base options
|
||||
************
|
||||
|
||||
:class:`VirtualDownloadView` inherits from
|
||||
:class:`~django_downloadview.views.base.DownloadMixin`, which has various
|
||||
options such as :attr:`~django_downloadview.views.base.DownloadMixin.basename`
|
||||
or :attr:`~django_downloadview.views.base.DownloadMixin.attachment`.
|
||||
|
||||
|
||||
***************************************
|
||||
Serve text (string or unicode) or bytes
|
||||
***************************************
|
||||
|
|
|
|||
Loading…
Reference in a new issue