Генерация PDF на Django

Здесь рассказывается как генерировать PDF с помощью представлений Django. Это возможно благодаря чудесной открытой библиотеке ReportLab.

Преимущество динамической генерации PDF заключается в том, например, что вы можете создавать разный документ для разных пользователей.

Например, Django использовали на kusports.com для генерации турнирной таблицы NCAA, которую можно распечатать как обычный PDF-файл.

Установка ReportLab

Библиотека ReportLab доступна на PyPI. Документация не случайно это PDF файл) также доступна для загрузки. Вы можете установить ReportLab с помощью pip:

$ pip install reportlab

Проверьте правильность установки с помощью импорта в интерактивном интерпретаторе:

>>> import reportlab

Если не вылезло никакой ошибки, значит установка прошла успешно.

Пишем собственное представление.

Ключевым фактором в динамической генерации PDF с помощью Django является то, что ReportLab API работает с файловыми объектами, также, как и HttpResponse.

Пример “Hello World”:

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"'

    # Create the PDF object, using the response object as its "file."
    p = canvas.Canvas(response)

    # 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, and we're done.
    p.showPage()
    p.save()
    return response

Код и комментарии должны быть очевидными, но несколько моментов заслуживают внимания:

  • Ответу устанавливается специальный тип application/pdf. Этим мы говорим браузеру, что результат запроса - документ PDF, а не HTML файл. Если этого не сделать, то браузер может попытаться интерпретировать его как HTML, что приведёт к ужасным, страшным абракадабрам в окне браузера.

  • Ответ получает дополнительный заголовок Content-Disposition, в котором указывается имя PDF-файла. Имя файла здесь произвольное - называйте его как хотите. Оно будет использовано браузером в диалоге “Сохранить как...” и других случаях.

  • Заголовок Content-Disposition в этом примере не зря начинается с 'attachment; ' . Это заставляет браузер показать всплывающее окно с выбором как же обрабатывать документ, даже если задано поведение по умолчанию. Если вы не укажите 'attachment;', браузер будет обрабатывать его плагинами, которые работают с PDF. Для этого нужно указать как-то так:

    response['Content-Disposition'] = 'filename="somefilename.pdf"'
    
  • Подключить ReportLab довольно просто: просто передайте response в качестве первого аргумента в canvas.Canvas. Класс Canvas ожидает любой файловый объект, а HttpResponse как раз прекрасно подходит на данную роль.

  • Следует отметить, что все последующие действия нужно производить с получившимся объектом (в данном случае с p), а не с response.

  • Наконец, важно вызвать showPage() и save().

Примечание

ReportLab не потокобезопасна. Пользователи могут мешать друг другу в процессе построения PDF.

Генерация сложных PDF

Если вы создаёте сложные PDF документы с помощью ReportLab, рассмотрите библиотеку io как временное хранилище для результата. Эта библиотека предоставляет файловый объект, который наиболее эффективен. Пример “Hello World”, переписанный на 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

Дополнительные ресурсы

  • PDFlib - ещё одна библиотека для генерации PDF с привязками для Python. Чтобы её использовать вместе с Django, используйте те же понятия, которые описаны в этой статье.

  • Pisa XHTML2PDF - ещё одна библиотека для генерации PDF. Пример как использовать её вместе с Django можно найти в её документации.

  • HTMLdoc - скрипт для конвертации HTML в PDF. У него нет интерфейса для Python, но вы можете избежать использования командной строки, используя system или popen для перенаправления вывода своих скриптов.

Другие форматы

Обратите внимание, что не так много специфики PDF в генерации документов - для ReportLab это всего лишь биты. Вы можете использовать подобную технику для генерации файла любого формата для которого есть соответствующая библиотека. В Генерация CSV на Django есть ещё примеры создания текстового формата.