django-renderpdf

django-renderpdf is a Django application for generating PDFs via Django.

Our intent is to properly integrate into Django, and have a very simple, yet flexible API. We follow common Django patterns, so rendering and exposing PDFs is simple.

Index

Installation

You’ll first need to install the python package:

pip install django-renderpdf

You don’t need to add it to INSTALLED_APPS.

This package depends on weasyprint, which will be installed as a dependency automatically. However, weasyprint itself has a few non-Python dependencies. Make sure the following works before proceeding:

python -c "import weasyprint"

If the above prints an error, please follow weasyprint’s installation documentation.

Usage

As a usage example, lets assume we have to print some shipping labels to dispatch a product. We’ll need to generate a PDF file for this to print.

from django.contrib.auth.mixins import LoginRequiredMixin
from django_renderpdf.views import PDFView

from myapp import Shipment


class LabelsView(LoginRequiredMixin, PDFView):
    """Generate labels for some Shipments.

    A PDFView behaves pretty much like a TemplateView, so you can treat it as such.
    """
    template_name = 'my_app/labels.html'

    def get_context_data(self, *args, **kwargs):
        """Pass some extra context to the template."""
        context = super().get_context_data(*args, **kwargs)

        context['shipments'] = Shipment.objects.filter(
            batch_id=kwargs['pk'],
        )

        return context

If anything in the above example seems completely new, I suggest you review the documentation for Django’s Class-based views.

You still need to include this view in your urls.py as usual:

from django.urls import path

path(
    '/shipments/labels/<int:pk>/',
    views.LabelsView.as_view(),
    name='shipment_labels',
)

Now visiting /shipments/labels/17, will return a PDF file which your browser will render. Note that, since we used the LoginRequiredMixin, anonymous users will be redirected to the usual login screen, and then back to this view after login.

API

PDFView

class django_renderpdf.views.PDFView(**kwargs)

A base class that renders requests as PDF files.

All views that render a PDF must inherit from this base class.

Usage and interface is quite similar to Django’s built-in views. Template context data can be defined via get_context_data just like with Django’s TemplateView subclasses.

The following additional attributes allow further customising behaviour of subclasses. These may be overridden by either an attribute or a property:

template_name: str | None = None

The name of the template file that will be rendered into a PDF file. Template discovery works just like any regular Django view.

allow_force_html: bool = True

Allow forcing this view to return a rendered HTML response, rather than a PDF response. If True, any requests with the URL parameter html=true will be rendered as plain HTML. This can be useful for debugging, but also allows reusing the same view for exposing both PDFs and HTML.

prompt_download: bool = False

If True, users will be prompted to download the PDF file, rather than have it rendered by their browsers.

This is achieved by setting the “Content-Disposition” HTTP header. If this attribute is True, then either download_name or get_download_name() must be defined.

download_name: str | None = None

When prompt_download is set to True, browsers will be instructed to prompt users to download the file, rather than render it.

In these cases, a default filename is presented. If you need custom filenames, you may override this attribute with a property:

@property
def download_name(self) -> str:
    return f"document_{self.request.kwargs['pk']}.pdf"

This attribute has no effect if prompt_download = False.

The following methods may also be overridden to further customise subclasses:

url_fetcher(url)

Returns the file matching URL.

This method will handle any URL resources that rendering HTML requires (e.g.: images in img tags, stylesheets, etc.).

The default behaviour will fetch any http(s) files normally, and will also attempt to resolve relative URLs internally.

Generally, you should not need to override this method unless you want to use custom URL schemes. For finer details on its inner workings, see weasyprint’s documentation on url_fetcher.

get_template_names() List[str]

Return a list of template names to be used for the request.

Must return a list. By default, just returns [self.template_name].

New in version 3.0.

get_download_name() str

Return the default filename when this file is downloaded.

Deprecated since version 3.0: Use download_name() as a property instead.

get_template_name() str

Return the name of the template which will be rendered into a PDF.

Deprecated since version 3.0: Use get_template_names() instead.

Helpers

django_renderpdf.helpers.render_pdf(template: ~typing.List[str] | str, file_: ~typing.IO, url_fetcher=<function django_url_fetcher>, context: dict | None = None)

Writes the PDF data into file_. Note that file_ can actually be a Django Response object as well, since these are file-like objects.

This function may be used as a helper that can be used to save a PDF file to a file (or anything else outside of a request/response cycle).

Parameters:
  • template – A list of templates, or a single template. If a list of templates is passed, these will be searched in order, and the first one found will be used.

  • file – A file-like object (or a Response) where to output the rendered PDF.

  • url_fetcher – See weasyprint’s documentation on url_fetcher.

  • context – Context parameters used when rendering the template.

Changelog

v4.1.0

  • Fixed documentation not rendering on readthedocs.

  • Add support for Python 3.11.

  • Add support for Django 4.1 and 4.2.

v4.0.0

  • Dropped support for Python 3.6.

  • Dropped support for Python 3.7.

  • Supported Python versions are now 3.8, 3.9 and 3.10.

  • Supported Django versions are now 3.2 and 4.0.

v3.0.1

  • render_pdf() may take a list of templates or a single template. This restores compatibility with pre-v3.0.0 interface.

v3.0.0

  • get_template_name has been deprecated in favour of get_template_names. This does not affect usages when template_name is defined.

  • get_download_name has been deprecated. Override download_name as a property instead.

v2.2.0

  • django_renderpdf.views.PDFView.url_fetcher is no longer a static method. If you were overriding this method, make sure you remove the @staticmethod decorator from your implementation.

  • Improved documentation at RTD.

v2.1.0

  • Add handling of relative URLs. CSS, image files, and other resources will be resolved using Django’s internal URL routing. This includes scenarios like serving static or media files via Django, or serving thing like custom css via custom Django views.

  • Drop support for Python 3.5.

v2.0.1

  • Improve handling of remote staticfiles.

v2.0.0

  • Support Python 3.7 and 3.8.

  • Support Django 2.2, 3.0 and 3.1.

  • Drop support for Django < 2.2.

Help

If you think you found a bug, or need help with something specific:

  • Please check existing issues for similar topics.

  • If there’s nothing relevant, please open a new issue describing your problem, and what you’ve tried so far.

Issues and source code are all in GitHub.

Background

django-renderpdf actually started out as code on multiple of my own projects (including some public ones).

After some time, it became clear that I’d been copy-pasting PDF-related bits across different projects, and since co-workers expressed interest in this design (using the Django templating system to generate PDFs), it finally made sense to move this into a separate library.

Donations

Donations are welcome. See here for further details.

Licence

django-renderpdf is licensed under the ISC licence.