使用 django 通过模板管理 pdf 并作为电子邮件附件发送

Posted

技术标签:

【中文标题】使用 django 通过模板管理 pdf 并作为电子邮件附件发送【英文标题】:Management of pdf with django through a template and sent as an attachment of an electronic mail 【发布时间】:2018-10-04 02:43:27 【问题描述】:

我正在使用 django 框架创建一个学生注册网站。

我到达了一个地方,我必须向用户显示表格,并且我收到治疗后输入的信息我必须向用户发送一封电子邮件,其中包含他输入的信息,将其放入 PDF 中我必须从包含上下文字典的模板生成。

目前,我只能通过HttpResponse 函数将其发送到浏览器来生成 PDF,但问题在于用户决定是否下载它,而我还有更多可以将手放在 pdf 上以作为附件发送。

我想打开一个数据库,其中会有一个models.FileField 类型的字段,通过库xhtml2pdf 生成带有上下文字典的pdf,我可以用信号将其保存在数据库中,然后通过将文件作为附件。

目前我尝试实现的所有方法都没有成功。这是我的代码:

utils.py

from io import BytesIO
from django.http import HttpResponse
from django.template.loader import get_template

from xhtml2pdf import pisa

def render_to_pdf(template_src, context_dict=):

    template = get_template(template_src)
    html = template.render(context_dict)
    result = BytesIO()
    pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result)
    if not pdf.err:
        return HttpResponse(result.getvalue(), content_type="application/pdf")
    return None

models.py

from django.conf import settings
from django.db.models.signals import pre_save, post_save

from django.core.mail import EmailMessage, EmailMultiAlternatives

from django.db import models

# Create your models here.


class MyModel(models.Model):
    order_id    = models.CharField(max_length=255)
    nom         = models.CharField(max_length=255)
    email       = models.EmailField()
    pdf         = models.FileField(upload_to="pdfs", null=True, blank=True)

def create_order_id(instance, new_order_id=None):

    order_id = instance.id

    if new_order_id is not None:
        order_id = new_order_id


    qs = MyModel.objects.filter(order_id=order_id)
    exists = qs.exists()

    if exists:
        new_order_id = "%s-%s" %(order_id.first().id)
        return create_order_id(instance, new_order_id)


    return order_id


def pre_save_receiver(sender, instance, *args, **kwargs):
    if not instance.order_id :
        instance.new_order_id = create_order_id(instance)




def send_mail_insciption(instance):

    subject = "Thank you"
    from_email = settings.EMAIL_HOST_USER
    to_email = [instance.email]
    body = "Votre inscription"

    email_pdf = EmailMultiAlternatives(

        subject = subject,
        body =body,
        from_email = from_email,
        to=to_email,    
        )


    email_pdf.attach_alternative(instance.pdf, "application/pdf")
    email_pdf.send()


def post_save_receiver(sender, instance, *args, **kwargs):
    send_mail_insciption(instance)




pre_save.connect(pre_save_receiver, sender=MyModel )
post_save.connect(post_save_receiver, sender=MyModel)


#views.py

def pdf_genarete(request):

    form = MyModelForm(request.POST or None)

    if form.is_valid():

        nom = form.cleaned_data.get("nom")
        email = form.cleaned_data.get("email")
        obj = MyModel.objects.create(nom=nom, email=email)

        context = "models_instance": obj

        pdf = render_to_pdf("pdfapp/template_pdf.html", context)
        filename = "mypdf_.pdf".format(obj.order_id)
        pdf.save()
        obj.pdf.save(filename, File(BytesIO(pdf.content)))

        return redirect(reverse("home"))

    return render(request, "pdfapp/formulaire.html", "form": form)

这里执行后终端报错:

 File "/home/michel/saintexupery/env/lib/python3.6/site-packages/django/db/models/base.py", line 769, in save_base
    update_fields=update_fields, raw=raw, using=using,
  File "/home/michel/saintexupery/env/lib/python3.6/site-packages/django/dispatch/dispatcher.py", line 178, in send
    for receiver in self._live_receivers(sender)
  File "/home/michel/saintexupery/env/lib/python3.6/site-packages/django/dispatch/dispatcher.py", line 178, in <listcomp>
    for receiver in self._live_receivers(sender)
  File "/home/michel/saintexupery/saintexupry/pdfapp/models.py", line 69, in post_save_receiver
    send_mail_insciption(instance)
  File "/home/michel/saintexupery/saintexupry/pdfapp/models.py", line 65, in send_mail_insciption
    email_pdf.send()
  File "/home/michel/saintexupery/env/lib/python3.6/site-packages/django/core/mail/message.py", line 294, in send
    return self.get_connection(fail_silently).send_messages([self])
  File "/home/michel/saintexupery/env/lib/python3.6/site-packages/django/core/mail/backends/smtp.py", line 110, in send_messages
    sent = self._send(message)
  File "/home/michel/saintexupery/env/lib/python3.6/site-packages/django/core/mail/backends/smtp.py", line 124, in _send
    message = email_message.message()
  File "/home/michel/saintexupery/env/lib/python3.6/site-packages/django/core/mail/message.py", line 254, in message
    msg = self._create_message(msg)
  File "/home/michel/saintexupery/env/lib/python3.6/site-packages/django/core/mail/message.py", line 440, in _create_message
    return self._create_attachments(self._create_alternatives(msg))
  File "/home/michel/saintexupery/env/lib/python3.6/site-packages/django/core/mail/message.py", line 450, in _create_alternatives
    msg.attach(self._create_mime_attachment(*alternative))
  File "/home/michel/saintexupery/env/lib/python3.6/site-packages/django/core/mail/message.py", line 393, in _create_mime_attachment
    Encoders.encode_base64(attachment)
  File "/usr/lib/python3.6/email/encoders.py", line 32, in encode_base64
    encdata = str(_bencode(orig), 'ascii')
  File "/home/michel/saintexupery/env/lib/python3.6/base64.py", line 534, in encodebytes
    _input_type_check(s)
  File "/home/michel/saintexupery/env/lib/python3.6/base64.py", line 520, in _input_type_check
    raise TypeError(msg) from err
TypeError: expected bytes-like object, not FieldFile

【问题讨论】:

请将您的错误的完整堆栈跟踪添加到您的问题中,以便我们可以尝试确定错误的来源 您是否正确生成了pdf文件?我可以向您展示一种无需将任何文件保存到数据库即可将其发送到电子邮件的方法,这是您想要的吗?在此之前,只需删除这个French Question(虽然我说法语,但我们是英文***,其他语言是不允许的),让我们认为这是真正的问题 @Mic 尝试将obj.pdf.save(filename, File(BytesIO(pdf.content))) 更改为obj.pdf.save(filename, BytesIO(pdf.content)) 当我应用此方法时,它们是相同的错误:上述异常(memoryview:需要类似字节的对象,而不是'FieldFile')是以下异常的直接原因 @AnjaneyuluBatta 我只是希望能够通过包含上下文字典的模板发送 pdf,如果有机会这样做,否则我愿意接受任何建议 【参考方案1】:

我是你的代码,你有这部分:

def send_mail_insciption(instance):
    ...
    email_pdf.attach_alternative(instance.pdf, "application/pdf")
    email_pdf.send()

错误消息(和回溯)说电子邮件需要字节作为附件,但instance.pdfFieldFile

尝试将文件转换为字节,可能使用instance.pdf.read(),如果这样可以解决问题,请告诉我们。

【讨论】:

当我在这里应用此方法时,它给出的错误是:ValueError at /pdfapp/pdf_genarete The 'pdf' 属性没有与之关联的文件。 嗯,这个错误一般表示文件不存在。如果在发送电子邮件之前添加print(instance.pdf.name, instance.pdf.size),会得到什么结果?它是否显示正确的文件? 您好!非常感谢您通过“读取”方法读取文件向我展示的方法有效。只是有些怪人知道我发送了附件,当我在手机上(在移动设备上)查阅我的邮箱时可以看到它,但是通过使用 PC 查阅我的电子邮件,我看不到附件。这怎么可能 ?你遇到过吗? @Mic 不,我没有遇到在您的 PC 中看不到附件的问题。

以上是关于使用 django 通过模板管理 pdf 并作为电子邮件附件发送的主要内容,如果未能解决你的问题,请参考以下文章

Django - 渲染模板并通过 AJAX 传递它们

我需要通过 django 管理面板上传模板,其中包含索引文件和静态文件

Python/Django - 我可以创建多个类似 pdf 文件的对象,将它们压缩并作为附件发送吗?

将 django 模板更改为具有 bootstrap 或 css 或两者的 PDF 的方法

如何在Django中将pdf作为电子邮件附件发送

django 生成动态的PDF文件