5
votes

I'm writing a Django function that takes some user input, and generates a pdf for the user. However, the process for generating the pdf is quite intensive, and I'll get a lot of repeated requests so I'd like to store the generated pdfs on the server and check if they already exist before generating them.

The problem is that django-wkhtmltopdf (which I'm using for generation) is meant to return to the user directly, and I'm not sure how to store it on the file.

I have the following, which works for returning a pdf at /pdf:

urls.py

urlpatterns = [
    url(r'^pdf$', views.createPDF.as_view(template_name='site/pdftemplate.html', filename='my_pdf.pdf'))
]

views.py

class createPDF(PDFTemplateView):
    filename = 'my_pdf.pdf'
    template_name = 'site/pdftemplate.html'

So that works fine to create a pdf. What I'd like is to call that view from another view and save the result. Here's what I've got so far:

#Create pdf
pdf = createPDF.as_view(template_name='site/pdftemplate.html', filename='my_pdf.pdf')
pdf = pdf(request).render()

pdfPath = os.path.join(settings.TEMP_DIR,'temp.pdf')
with open(pdfPath, 'w') as f:
    f.write(pdf.content)

This creates temp.pdf and is about the size I'd expect but the file isn't valid (it renders as a single completely blank page).

Any suggestions?

2
Seems it should be with open(pdfPath, 'wb') as f:, i.e., you need to add b because the rendered output will be in byte formatmehmet

2 Answers

3
votes

Elaborating on the previous answer given: to generate a pdf file and save to disk do this anywhere in your view:

            ...
            context = {...}  # build your context

            # generate response
            response = PDFTemplateResponse(
                            request=self.request,
                            template=self.template_name,
                            filename='file.pdf',
                            context=context,
                            cmd_options={'load-error-handling': 'ignore'})

            # write the rendered content to a file
            with open("file.pdf", "wb") as f:
                f.write(response.rendered_content)
            ...

I have used this code in a TemplateView class so request and template fields were set like that, you may have to set it to whatever is appropriate in your particular case.

1
votes

Well, you need to take a look to the code of wkhtmltopdf, first you need to use the class PDFTemplateResponse in wkhtmltopdf.views to get access to the rendered_content property, this property get us access to the pdf file:

response = PDFTemplateResponse(
        request=<your_view_request>,
        template=<your_template_to_render>,
        filename=<pdf_filename.pdf>,
        context=<a_dcitionary_to_render>,
        cmd_options={'load-error-handling': 'ignore'})

Now you could use the rendered_content property to get access to the pdf file:

 mail.attach('pdf_filename.pdf', response.rendered_content, 'application/pdf')

In my case I'm using this pdf to attach to an email, you could store it.