【问题标题】:Django CBV: Programmatically save HTML page to PDF file on the serverDjango CBV:以编程方式将 HTML 页面保存到服务器上的 PDF 文件
【发布时间】:2018-03-11 21:17:27
【问题描述】:

访问相关的url,呈现一个html页面。我需要以编程方式将此 html 页面保存到服务器上的 pdf 文件中。

views.py

class PdfView(DetailView):
    model = TheModel
    template_name = 'pdf.html'

urls.py

urlpatterns = [
    url(
        r'^(?P<pk>[0-9]+)/$',
        PdfDataView.as_view(),
        name='pdf-data',
    ),
]

【问题讨论】:

  • 总是包含与版本无关的python标签。
  • @raratiru 我的回答对你有用吗?
  • @andilabs 我在几分钟前看到它并点赞,谢谢!我明白了。我目前正试图通过等式的“保存到服务器”位来维持生计,this approach 之类的东西似乎是个好主意。
  • @raratiru 如果对你有帮助,请点击接受✅

标签: python django python-3.x django-views


【解决方案1】:

以下解决方案在 HTTP 请求期间保存网页的 pdf 版本,然后将 HTTP 响应提供给浏览器。

我使用了weasyprint,因为它能够开箱即用地处理 unicode 字符。

views.py

from weasyprint import HTML

class PdfView(DetailView):
    model = TheModel
    template_name = 'pdf.html'

    def get(self, request, *args, **kwargs):
        template_response = super().get(self, request, *args, **kwargs)
        HTML(string=template_response.rendered_content).write_pdf(
            '/path/to/test.pdf',
            stylesheets=['/path/to/pdf.css']
        )
        return template_response

在视图之外,更好的解决方案是使用requests 获取 HTML 页面,然后创建 pdf 文件。这样做更好,因为服务器不必等待 pdf 被创建,可能会阻塞其他等待服务器的请求:

>>>import requests
>>>resp = requests.get('http://my-url/object_id')
>>>HTML(string=resp.content).write_pdf('/path/to/test.pdf', stylesheets=['/path/to/pdf.css'])

【讨论】:

    【解决方案2】:

    尝试以下方法。虽然它通过响应将服务文件呈现给最终用户,但是修改它以在服务器上写入文件对您来说应该很容易:

    功能视图:

    def pdf_sticker(request, pk):
        spot = get_object_or_404(Spot, pk=pk)
        if spot.is_certificated:
            pdf, result = render_to_pdf(
                'www/pdf_sticker.html',
                {
                    'pass_smth': 'needed_in_render',
                    'MEDIA_ROOT': settings.MEDIA_ROOT,
                    'STATIC_ROOT': settings.STATICFILES_DIRS[0],
                    'pagesize': 'A6',
                }
            )
            if not pdf.err:
                return HttpResponse(result.getvalue(), content_type='application/pdf')
            return HttpResponse('We had some errors')
        else:
            raise Http404
    

    辅助方法:

    from io import StringIO, BytesIO
    from xhtml2pdf import pisa
    
    from django.template.loader import get_template
    
    
    def render_to_pdf(template_src, context_dict):
        template = get_template(template_src)
        html = template.render(context_dict)
        result = BytesIO()
        pdf = pisa.pisaDocument(
            StringIO(html),
            dest=result,
            encoding='UTF-8'
        )
    
        return pdf, result
    

    模板:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
        <head>
    
    
            <title>Sticker for certificated spot</title>
            <style type="text/css">
                     @font-face {
                        font-family: "Lobster";
                        src: url("{{ STATIC_ROOT }}/font/Lobster-32.ttf");
                        font-weight: normal;
                        font-style: normal;
                    }
                    @font-face {
                        font-family: "Lato";
                        src: url("{{ STATIC_ROOT }}/font/Lato-Hairline.ttf");
                        font-weight: 100;
                        font-style: thin;
                    }
                    @font-face {
                        font-family: "Nobile";
                        src: url("{{ STATIC_ROOT }}/font/Nobile-34.ttf");
                        font-weight: normal;
                        font-style: normal;
                    }
    
                    @page {
                        size: {{ pagesize }};
                        margin: 0.5cm;
                    }
    
            </style>
        </head>
        <body>
            <center>
                <img src="{{ STATIC_ROOT }}/{{ SPOT_PROJECT_NAME }}/certificate.png" height="260px">
                <p style="font-family:Lobster;">
                    <span style="font-size:60px;">
                        {% settings_value "SPOT_PROJECT_SUBJECT" %}<br>
                    </span>
                    <span style=" font-size:32px;">
                        friendly spot!
                    </span>
                    <br>
                    <table>
                        <tr>
                            <td colspan=2>
                                 <img src="http://{{BASE_HOST}}{% url 'www:qrencode_link' pk=spot.pk size=4 %}">
                            </td>
                        </tr>
                        <tr>
                            <td colspan=2 height=5></td>
                        </tr>
                        <tr>
                            <td colspan=2>
                                <span style="font-family:Nobile; font-size:15px;">
                                Powered by: <img src="{{ STATIC_ROOT }}/{{ SPOT_PROJECT_NAME }}/logo.png" height="50px"> </span>
                                <span style="font-family:Lobster; font-size:25px;">
                                        {{ SPOT_PROJECT_NAME }}
                                </span>
                            </td>
                        </tr>
                    </table>
                </p>
            </center>
        </body>
    </html>
    

    适用于 Python 3.6、Django 2.0。

    库版本:

    Django==2.0.2
    xhtml2pdf==0.2b1
    

    关于将其写入服务器上的本地文件的部分,这可能会有所帮助:

    http://xhtml2pdf.readthedocs.io/en/latest/usage.html#using-with-python-standalone

    【讨论】:

    • 感谢您的回复。我不会使用html2pdf,因为weasyprint 似乎很容易处理开箱即用的unicode 字符。还uses one line创建pdf文件,似乎效率更高。
    【解决方案3】:

    这个功能对我有用。

    def render_to_pdf(template_src, context_dict={}):
            template = get_template(template_src)
            html = template.render(context_dict)
            result = BytesIO()
            pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result)
            if not pdf.err:
                f = open('test2.pdf', 'wb')
                myfile = File(f)
                myfile.write(result.getvalue())
                return HttpResponse(result.getvalue(), content_type='application/pdf')
            return None
    

    【讨论】:

      猜你喜欢
      • 2014-09-05
      • 1970-01-01
      • 2012-12-07
      • 1970-01-01
      • 2013-04-30
      • 2021-04-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多