Создать PDF из HTML-шаблона и отправить по электронной почте в Django

Я пытаюсь сгенерировать файл PDF из шаблона HTML с помощью пакета Python Weasyprint, и мне нужно отправить его по электронной почте, используя.

Вот что я попробовал:

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')

Но он возвращает ошибку как TypeError: expected bytes-like object, not HttpResponse

Как я могу сгенерировать и отправить файл PDF на электронную почту из шаблона HTML?

Обновление: с этим обновленным кодом теперь он генерирует PDF и отправляет электронное письмо, но когда я открываю прикрепленный файл PDF из полученного электронного письма, он говорит unsupported file formate data,

Вот обновленный код:

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 = 'arycloud7@icloud.com'
to_emails = ['abdul12391@gmail.com', 'arycloud7@gmail.com']
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')

Помоги мне, пожалуйста!

Заранее спасибо!

4 ответа

Решение

Вот полная рабочая версия приведенного выше кода:

    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()

Как видно из документа Weasysprint, вызов метода write_pdf() окажет документ в одном File,

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

Получив HTML-объект, вызовите его метод write_pdf() или write_png(), чтобы получить отрендеренный документ в одном файле PDF или PNG.

Кроме того, они упоминают, что

Без аргументов эти методы возвращают байтовую строку в памяти.

Таким образом, вы можете получить его байтовую строку PDF и использовать ее для прикрепления или передать имя файла для записи в PDF.

Есть момент, когда вы также можете отправить записываемый файлоподобный объект write_pdf(),

Если вы передадите имя файла или записываемый файл-подобный объект, они будут записывать туда напрямую.

Вы можете создать и прикрепить файл PDF следующим образом:

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

Вы также можете отправить 200 Ответов, если он был успешным, или 500, если он не прошел.

ПРИМЕЧАНИЕ о SMTP-сервере

Обычно вам нужен SMTP почтовый сервер для ретрансляции вашего сообщения в пункт назначения.

Как вы можете прочитать из Django документа send_mail нужна некоторая конфигурация:

Почта отправляется с использованием SMTP-хоста и порта, указанных в настройках EMAIL_HOST и EMAIL_PORT. Настройки EMAIL_HOST_USER и EMAIL_HOST_PASSWORD, если они установлены, используются для аутентификации на SMTP-сервере, а настройки EMAIL_USE_TLS и EMAIL_USE_SSL определяют, используется ли безопасное соединение.

Тогда вы можете использовать send_mail() со следующими параметрами для ретрансляции вашего сообщения на локальный SMTP-сервер.

send_mail (тема, сообщение, от_почты, список получателей, fail_silently=False, auth_user=None, auth_password=None, connection=None, html_message=None)

Внимание: не пропустите параметры аутентификации.

Этот код работает для меня

          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 = ['receiver@gmail.com']
    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)

Основываясь на ответе @Rishabh gupta:

      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=("youremailadress@gmail.com",),
    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

Другие вопросы по тегам