1
0
mirror of https://github.com/django/django.git synced 2025-10-24 06:06:09 +00:00

Fixed #16470 -- Allowed FileResponse to auto-set some Content headers.

Thanks Simon Charette, Jon Dufresne, and Tim Graham for the reviews.
This commit is contained in:
Claude Paroz
2018-05-15 18:12:11 +02:00
committed by GitHub
parent 2dcc5d629a
commit a177f854c3
6 changed files with 192 additions and 82 deletions

View File

@@ -41,21 +41,21 @@ Write your view
===============
The key to generating PDFs dynamically with Django is that the ReportLab API
acts on file-like objects, and Django's :class:`~django.http.HttpResponse`
objects are file-like objects.
acts on file-like objects, and Django's :class:`~django.http.FileResponse`
objects accept file-like objects.
Here's a "Hello World" example::
from django.http import HttpResponse
import io
from django.http import FileResponse
from reportlab.pdfgen import canvas
def some_view(request):
# Create the HttpResponse object with the appropriate PDF headers.
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
# Create a file-like buffer to receive PDF data.
buffer = io.BytesIO()
# Create the PDF object, using the response object as its "file."
p = canvas.Canvas(response)
# Create the PDF object, using the buffer as its "file."
p = canvas.Canvas(buffer)
# Draw things on the PDF. Here's where the PDF generation happens.
# See the ReportLab documentation for the full list of functionality.
@@ -64,37 +64,35 @@ Here's a "Hello World" example::
# Close the PDF object cleanly, and we're done.
p.showPage()
p.save()
return response
# FileResponse sets the Content-Disposition header so that browsers
# present the option to save the file.
return FileResponse(buffer, as_attachment=True, filename='hello.pdf')
The code and comments should be self-explanatory, but a few things deserve a
mention:
* The response gets a special MIME type, :mimetype:`application/pdf`. This
tells browsers that the document is a PDF file, rather than an HTML file.
If you leave this off, browsers will probably interpret the output as
HTML, which would result in ugly, scary gobbledygook in the browser
window.
* The response will automatically set the MIME type :mimetype:`application/pdf`
based on the filename extension. This tells browsers that the document is a
PDF file, rather than an HTML file or a generic `application/octet-stream`
binary content.
* The response gets an additional ``Content-Disposition`` header, which
contains the name of the PDF file. This filename is arbitrary: Call it
whatever you want. It'll be used by browsers in the "Save as..." dialog, etc.
* When ``as_attachment=True`` is passed to ``FileResponse``, it sets the
appropriate ``Content-Disposition`` header and that tells Web browsers to
pop-up a dialog box prompting/confirming how to handle the document even if a
default is set on the machine. If the ``as_attachment`` parameter is omitted,
browsers will handle the PDF using whatever program/plugin they've been
configured to use for PDFs.
* The ``Content-Disposition`` header starts with ``'attachment; '`` in this
example. This forces Web browsers to pop-up a dialog box
prompting/confirming how to handle the document even if a default is set
on the machine. If you leave off ``'attachment;'``, browsers will handle
the PDF using whatever program/plugin they've been configured to use for
PDFs. Here's what that code would look like::
* You can provide an arbitrary ``filename`` parameter. It'll be used by browsers
in the "Save as..." dialog.
response['Content-Disposition'] = 'filename="somefilename.pdf"'
* Hooking into the ReportLab API is easy: Just pass ``response`` as the
first argument to ``canvas.Canvas``. The ``Canvas`` class expects a
file-like object, and :class:`~django.http.HttpResponse` objects fit the
bill.
* Hooking into the ReportLab API is easy: The same buffer passed as the first
argument to ``canvas.Canvas`` can be fed to the
:class:`~django.http.FileResponse` class.
* Note that all subsequent PDF-generation methods are called on the PDF
object (in this case, ``p``) -- not on ``response``.
object (in this case, ``p``) -- not on ``buffer``.
* Finally, it's important to call ``showPage()`` and ``save()`` on the PDF
file.
@@ -105,42 +103,6 @@ mention:
with building PDF-generating Django views that are accessed by many people
at the same time.
Complex PDFs
============
If you're creating a complex PDF document with ReportLab, consider using the
:mod:`io` library as a temporary holding place for your PDF file. This
library provides a file-like object interface that is particularly efficient.
Here's the above "Hello World" example rewritten to use :mod:`io`::
from io import BytesIO
from reportlab.pdfgen import canvas
from django.http import HttpResponse
def some_view(request):
# Create the HttpResponse object with the appropriate PDF headers.
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
buffer = BytesIO()
# Create the PDF object, using the BytesIO object as its "file."
p = canvas.Canvas(buffer)
# Draw things on the PDF. Here's where the PDF generation happens.
# See the ReportLab documentation for the full list of functionality.
p.drawString(100, 100, "Hello world.")
# Close the PDF object cleanly.
p.showPage()
p.save()
# Get the value of the BytesIO buffer and write it to the response.
pdf = buffer.getvalue()
buffer.close()
response.write(pdf)
return response
Other formats
=============