问题
Visiting the relevant url, an html page is rendered. I need to programmatically save this html page to a pdf file on the server.
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',
),
]
回答1:
Try an approach like below. Although it presents serving file to the end user via response, but modifying it to write file on server should be easy for you:
The functional view:
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
The helper method:
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
the template:
<!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>
Working for Python 3.6, Django 2.0.
Libraries versions:
Django==2.0.2
xhtml2pdf==0.2b1
Regarding the part of writing it to local file on server this might help:
http://xhtml2pdf.readthedocs.io/en/latest/usage.html#using-with-python-standalone
回答2:
The following solution saves a pdf version of the web page during the HTTP request, before the HTTP response is served to the browser.
I used weasyprint because it managed to take care of the unicode characters out of the box.
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
A better solution, outside the view, would be to use requests to fetch the HTML page and then create the pdf file. It is better because the server will not have to wait for the pdf to be created, potentially blocking other requests waiting to be serverd:
>>>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'])
来源:https://stackoverflow.com/questions/49225195/django-cbv-programmatically-save-html-page-to-pdf-file-on-the-server