Any way to create PDF file with wkhtmltopdf without returning HttpResponse or using URL? I just want to attach PDF file to email
Question:
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.
Answers:
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.
Hey Please check your wkhtmltopdf binary is it executable or not. as this PDFTemplateResponse worked for me.
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.
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.
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.
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.
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.
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.
Hey Please check your wkhtmltopdf binary is it executable or not. as this PDFTemplateResponse worked for me.
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.
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.
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.
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.