Abdul Rehman
Abdul Rehman

Reputation: 5684

Generate PDF from html template and send via Email in Django

I'm trying to generate a pdf file from an HTML template using Weasyprint python package and I need to send it via email using.

Here's what i have tried:

def send_pdf(request):
minutes = int(request.user.tagging.count()) * 5
testhours = minutes / 60
hours = str(round(testhours, 3))
user_info = {
    "name": str(request.user.first_name + ' ' + request.user.last_name),
    "hours": str(hours),
    "taggedArticles": str(request.user.tagging.count())
}
html = render_to_string('users/certificate_template.html',
                        {'user': user_info})
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'filename=certificate_{}'.format(user_info['name'] + '.pdf')
pdf = weasyprint.HTML(string=html).write_pdf(response, )
from_email = 'our_business_email_address'
to_emails = ['Reciever1', 'Reciever2']
subject = "Certificate from INC."
message = 'Enjoy your certificate.'
email = EmailMessage(subject, message, from_email, to_emails)
email.attach("certificate.pdf", pdf, "application/pdf")
email.send()
return HttpResponse(response, content_type='application/pdf')

But it returns an error as TypeError: expected bytes-like object, not HttpResponse

How can I generate and send a pdf file to an email from HTML template?

Update: With this updated code now it's generating pdf and sending an email but when I open attached pdf file from recieved email it says unsupported file formate data.

Here's the updated Code:

def send_pdf(request):
minutes = int(request.user.tagging.count()) * 5
testhours = minutes / 60
hours = str(round(testhours, 3))
user_info = {
    "name": str(request.user.first_name + ' ' + request.user.last_name),
    "hours": str(hours),
    "taggedArticles": str(request.user.tagging.count())
}
html = render_to_string('users/certificate_template.html',
                        {'user': user_info})
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'filename=certificate_{}'.format(user_info['name']) + '.pdf'
pdf = weasyprint.HTML(string=html).write_pdf()
from_email = '[email protected]'
to_emails = ['[email protected]', '[email protected]']
subject = "Certificate from Nami Montana"
message = 'Enjoy your certificate.'
email = EmailMessage(subject, body=pdf, from_email=settings.EMAIL_HOST_USER, to=to_emails)
# email.attach("certificate.pdf", pdf, "application/pdf")
email.content_subtype = "pdf"  # Main content is now text/html
email.encoding = 'ISO-8859-1'
email.send()
return HttpResponse(pdf, content_type='application/pdf')

Help me, please!

Thanks in advance!

Upvotes: 4

Views: 11367

Answers (4)

run_the_race
run_the_race

Reputation: 2418

Building on @Rishabh gupta answer:

import io

from django.template.loader import render_to_string
from django.core.mail import EmailMultiAlternatives
from weasyprint import HTML

context = { "name": 'Hello', }
html_string = render_to_string('myapp/report.html', context)
html = HTML(string=html_string)
buffer = io.BytesIO()
html.write_pdf(target=buffer)
pdf = buffer.getvalue()

email_message = EmailMultiAlternatives(
    to=("[email protected]",),
    subject="subject test print",
    body="heres is the body",
)
filename = 'test.pdf'
mimetype_pdf = 'application/pdf'
email_message.attach(filename, pdf, mimetype_pdf)
email_message.send(fail_silently=False)  # TODO zzz mabye change this to True

Upvotes: 0

Rishabh Gupta
Rishabh Gupta

Reputation: 87

This code works for me

    template = get_template('admin/invoice.html')
    context = {
        "billno": bill_num,
        "billdate": bill_date,
        "patientname": patient_name,
        "totalbill": total_bill,
        "billprocedure": invoice_cal,

    }

    html  = template.render(context)
    result = BytesIO()
    pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result)#, link_callback=fetch_resources)
    pdf = result.getvalue()
    filename = 'Invoice.pdf'
    to_emails = ['[email protected]']
    subject = "From CliMan"
    email = EmailMessage(subject, "helloji", from_email=settings.EMAIL_HOST_USER, to=to_emails)
    email.attach(filename, pdf, "application/pdf")
    email.send(fail_silently=False)

Upvotes: 1

Abdul Rehman
Abdul Rehman

Reputation: 5684

Here's the complete working version of above code:

    user_infor = ast.literal_eval(ipn_obj.custom)
    if int(user_infor['taggedArticles']) > 11:
        # generate and send an email with pdf certificate file to the user's email
        user_info = {
            "name": user_infor['name'],
            "hours": user_infor['hours'],
            "taggedArticles": user_infor['taggedArticles'],
            "email": user_infor['email'],
        }
        html = render_to_string('users/certificate_template.html',
                                {'user': user_info})
        response = HttpResponse(content_type='application/pdf')
        response['Content-Disposition'] = 'filename=certificate_{}'.format(user_info['name']) + '.pdf'
        pdf = weasyprint.HTML(string=html, base_url='http://8d8093d5.ngrok.io/users/process/').write_pdf(
            stylesheets=[weasyprint.CSS(string='body { font-family: serif}')])
        to_emails = [str(user_infor['email'])]
        subject = "Certificate from Nami Montana"
        email = EmailMessage(subject, body=pdf, from_email=settings.EMAIL_HOST_USER, to=to_emails)
        email.attach("certificate_{}".format(user_infor['name']) + '.pdf', pdf, "application/pdf")
        email.content_subtype = "pdf"  # Main content is now text/html
        email.encoding = 'us-ascii'
        email.send()

Upvotes: 5

H.Tibat
H.Tibat

Reputation: 373

As you can see from Weasysprint document, calling method write_pdf() will render the document in a single File.

http://weasyprint.readthedocs.io/en/stable/tutorial.html

Once you have a HTML object, call its write_pdf() or write_png() method to get the rendered document in a single PDF or PNG file.

Also, they mention that

Without arguments, these methods return a byte string in memory.

So, you can get its PDF byte string and use it for attachment or pass the filename to write the PDF to.

There is a point that you can also send a writable file-like object to write_pdf().

If you pass a file name or a writable file-like object, they will write there directly instead.

You can generate and attach the PDF file like this:

pdf = weasyprint.HTML(string=html).write_pdf()
...
email.attach("certificate.pdf", pdf, "application/pdf")

You can also send 200 Response if it was successful or 500 if it failed.

NOTE About SMTP Server

Normally you need an SMTP mail server for relaying your message to your destination.

As you can read from Django document send_mail need some configuration:

Mail is sent using the SMTP host and port specified in the EMAIL_HOST and EMAIL_PORT settings. The EMAIL_HOST_USER and EMAIL_HOST_PASSWORD settings, if set, are used to authenticate to the SMTP server, and the EMAIL_USE_TLS and EMAIL_USE_SSL settings control whether a secure connection is used.

Then you can use send_mail() with following parameters for relaying your message to the local SMTP server.

send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None, connection=None, html_message=None)

Attention: Don't miss authentication parameters.

Upvotes: 3

Related Questions