Help us understand the problem. What is going on with this article?

DjangoのDetailViewをPDFで出力する(日本語対応)

More than 3 years have passed since last update.

DjangoでgenericのDetailViewを使って日本語PDF出力をする方法です。

template htmlからPDFを出力したいのでxhtml2pdfというライブラリを使用します。
python3ではdevバージョンしか動かないので気をつけてください。

pip3 install --pre xhtml2pdf

まず使いたい日本語フォントを用意します。
次にフォントを<prj_name>/<prj_name>/static/fontsに設置します。
設置したらsettings.pyにpathを通します。

prj_name/prj_name/settings.py
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, '<prj_name>', 'static')

次にviewを書いていきます。
今回は例としてfoo_app(<prj_name>/foos)のモデルFooのDetailViewをPDFとして出力します。
generic.DetailViewを使いrender_to_responseの部分だけoverrideします。

prj_name/foos/views.py
import io
import os
from django.conf import settings
from django.views import generic
from django.http import HttpResponse
from django.template.loader import get_template
from xhtml2pdf import pisa

class FooPDFView(generic.DetailView):
    model = Foo
    template_name = 'foos/pdf.html'

    def render_to_response(self, context):
        html = get_template(self.template_name).render(self.get_context_data())
        result = io.BytesIO()
        pdf = pisa.pisaDocument(
            io.BytesIO(html.encode('utf-8')),
            result,
            link_callback=link_callback,
            encoding='utf-8',
        )

        if not pdf.err:
            return HttpResponse(
                result.getvalue(),
                content_type='application/pdf'
            )

        return HttpResponse('<pre>%s</pre>' % escape(html))

def link_callback(uri, rel):
    sUrl = settings.STATIC_URL
    sRoot = settings.STATIC_ROOT
    path = os.path.join(sRoot, uri.replace(sUrl, ""))

    if not os.path.isfile(path):
        raise Exception(
            '%s must start with %s' % \
            (uri, sUrl)
        )

    return path

templateを書いていきます。
フォントを設定しないと日本語は予期している通りに表示されないので気をつけてください。

prj_name/foos/templates/foos/pdf.html
<!DOCTYPE html>{% load static %}
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <style type="text/css">
    @font-face {
      font-family: "japanese";
      src: url("{% static 'fonts/<your_font>.ttf' %}");
    }
    @page {
      size: A4;
      font-family: "japanese";
    }
    html, body {
      font-family: "japanese";
    }
  </style>
</head>
<body>
  <div>
    名前 {{ object.name }}
  </div>
</body>
</html>

urlsを設定します。

prj_name/foos/urls.py
app_name = 'foos'
urlpatterns = [
    url(r'^(?P<pk>[0-9]+)/pdf/$',views.FooPDFView.as_view(), name='foo-pdf'),
]

projectのurlsに通します。

prj_name/prj_name/urls.py
urlpatterns = [
    url(r'^foos/', include('foos.urls')),
]

これで/foos/:foo_id/pdfにアクセスするとPDFが出力されると思います。

shinonomeinc
東京理科大学発ベンチャー。提携大学内にソフトウェア研究所を組織し、学生向けのTech教育を提供しています。
http://shinonome.io
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした