Any way to create PDF file with wkhtmltopdf without returning HttpResponse or using URL? I just want to attach PDF file to email
Asked Answered
D

7

10

I am using wkhtmltopdf wrapper to generate template into PDF in Django 1.6. It works fine when I want to display the PDF afterwards or send the PDF file with HttpResponse for download but what I want to do is to create the file in my tmp folder and attach it to an email.

I am not sure how to achieve this.

# views.py

context = {
    'products_dict': products_dict,
    'main_categories': main_categories,
    'user_category': user_category
}

response = PDFTemplateResponse(request=request,
                               context=context,
                               template="my_template.html",
                               filename="filename.pdf",
                               show_content_in_browser=True,
                               cmd_options={'encoding': 'utf8',
                                            'quiet': True,
                                            'orientation': 'landscape',
                                           }
                               )

return response

The code above generate the PDF exactly how I want it. The thing is I don't want to display the PDF in the browser or start a download (I don't want to return response). I just want to create it and then attach the file to an email like this:

email = EmailMessage()
email.subject = "subject"
email.body = "Your PDF"
email.from_email = "[email protected]"
email.to = [ "[email protected]", ]

# Attach PDF file to the email
email.attach_file(my_pdf_file_here)
# Send email
email.send()

I tried to use subprocess but it doesn't seem like I can send context to my template to render it before generating the PDF.

Dawn answered 18/6, 2014 at 15:26 Comment(6)
You can do this by encode pdf file with base64 and appending to message body.Joel
Attaching the file to the email is not a problem. The problem is to create the PDF file with wkhtmltopdf without returning an HttpResponse or using a URL/Class-based viewDawn
Oh! Im sorry. Can't you just write output of the wkhtmtopdf to into a file in binary mode?Joel
I don't know how to produce the PDF output in my view without returning HttpResponseDawn
In your example, return is not looks like HttpResponse. Unless if wkhtmltopdf output is. If output of wk is HttpResponse object I guess you need to do some workaround.Joel
Can you checkout the type of response object?Joel
A
5

Your issue is not with wkhtmltopdf, but the django-wkhtmltopdf which provides some class-based views that it renders with wkhtmltopdf. If you don't want a view, you don't need to use them: you could just render the template yourself and pass the result string to the command-line wkhtmltopdf tool.

It looks like the django-wkhtmltopdf library does provide some utility functions (in the utils directory) which might make that last stage a bit easier.

Aeromarine answered 18/6, 2014 at 16:0 Comment(5)
This is exactly what I was looking for ! from here: github.com/incuna/django-wkhtmltopdf/blob/master/wkhtmltopdf/… I could use the wkhtmltopdf function. One question though, How can I render my template with my context variables before passing it to the wkhtmltopdf function?Dawn
You can use the render_to_string shortcut.Aeromarine
I'm confused what to do with the string afterwards... I need to provide a file path or URL of the html to be converted to the wkhtmltopdf function.Dawn
You can use a tempfile.NamedTemporaryFile, as they do in the view.Aeromarine
I ended up using render_to_temporary_file method. Check my EDIT for what I did. Thank you Daniel !Dawn
L
2

My Work around
The problem here, while sending mail with pdf file attached is, email.attach takes bytes-like object, but we have pdftemplate response object.

I tried to convert it to bytes by few ways, but ended up with errors. So, i just randomly went into the definition of PDFTemplateResponse class, then I found rendered_content method in it, thanks to vs code for making it easier.
Then what i just did is write the below line, and it worked!

email.attach("mypdf.pdf", response.rendered_content, 'application/pdf')

PS: This is the first time I am posting something on stack overflow. So, pardon my mistakes.

Lozenge answered 31/1, 2022 at 9:37 Comment(0)
P
0

The answer provided has become outdated as render_to_temporary_file is no longer a method of PDFTemplateResponse.

I haven't attempted to email PDFs, but to save a generated PDF to a Django model's file field, you can proceed as follows:

from wkhtmltopdf.views import PDFTemplateResponse
from io import BytesIO
from django.core.files import File
from django.http import HttpRequest
    
request = HttpRequest()

context = {
   'order' : order,
   'items' : order.ordered_products.all()
}

response = PDFTemplateResponse(
    request=request, 
    context=context, 
    template="products/order_pdf.html",
    cmd_options={
        'encoding': 'utf8',
        'quiet': True
    }
)
    
my_model.pdf_field.save(
        response.filename, File(BytesIO(response.rendered_content)))

It should be easy to email the PDF from there.

Pensile answered 17/11, 2021 at 4:55 Comment(0)
U
0

2023: looking through wkhtmltopdf.utils I found this. Much simpler.

from wkhtmltopdf.utils import render_pdf_from_template

# Generate a pdf from template
pdf = render_pdf_from_template('template.html',
                                    None, None,
                                    context=context)

# Save the pdf somewhere
with open('test.pdf', "w+b") as f:
    f.write(pdf)

# Return a response
return render(request, 'index.html')
Uria answered 24/5, 2023 at 13:32 Comment(0)
L
-1

Hey Please check your wkhtmltopdf binary is it executable or not. as this PDFTemplateResponse worked for me.

Leap answered 11/9, 2014 at 7:29 Comment(0)
U
-1
response = PDFTemplateResponse(
    request,
    template='reports/report_email.html',
    filename='weekly_report.pdf',
    context=render_data,
    cmd_options={'load-error-handling': 'ignore'})



subject, from_email, to = 'reports', '[email protected]', '[email protected]'
html_content = render_to_string('reports/report_email.html',render_data)
text_content = strip_tags(html_content)
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
msg.attach_alternative(html_content, "text/html")
msg.attach('pdf_filename.pdf', response.rendered_content, 'application/pdf')
msg.send()

this code snippet may help you after placing it in views.py.

Umbles answered 14/12, 2015 at 11:41 Comment(0)
M
-1

Thanks to Daniel Roseman for the help to go towards what I wanted. I did use the tests file of wkhtmltopdf here: http://pydoc.net/Python/django-wkhtmltopdf/1.1/wkhtmltopdf.tests.tests/

This is what I did in my view:

order = Order.objects.get(id=order_id)
return_file = "tmp/" + 'order' + str(order_id) + '.pdf'

context = {
    'order' : order,
    'items' : order.ordered_products.all()
}

response = PDFTemplateResponse(
    request=request, 
    context=context, 
    template="'products/order_pdf.html'",
    cmd_options={
        'encoding': 'utf8',
        'quiet': True
    }
)

temp_file = response.render_to_temporary_file("products/order_pdf.html")

wkhtmltopdf(pages=[temp_file.name], output=return_file)

You can then use the return_file in email.attach() method like this:

email.attach_file(return_file)

You can also omit output parameter in the wkhtmltopdf method. Then the method will return the output and use this output in attach_file() method.


This answer was posted as an edit to the question Any way to create PDF file with wkhtmltopdf without returning HttpResponse or using URL? I just want to attach PDF file to email by the OP dguay under CC BY-SA 3.0.

Mielke answered 22/12, 2022 at 20:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.