【问题标题】:Sending a text + HTML email with a calendar ICS attachment in Django or Python在 Django 或 Python 中发送带有日历 ICS 附件的文本 + HTML 电子邮件
【发布时间】:2021-09-13 20:51:28
【问题描述】:

我一直在寻找一个库或者至少是功能性的 sn-p 代码,它可以让我从 Django(或至少在 Python 中)发送一封电子邮件,其中包含文本内容、HTML 内容和 ICS 日历附件每个主要的电子邮件客户端都认可。对于我的特定用例,只要向用户提供“添加到日历”按钮就足够了。

我觉得现在这应该是一个已解决的问题,但我只找到引用未维护或以其他方式过时或不完整的库的答案。我已经测试了几个可以附加 ICS 文件的 sn-ps,但 G-mail 并没有像往常那样让我选择将其添加到日历中。

我缺少现成的解决方案吗?

【问题讨论】:

  • 我有@bdbd,它已经过时了,已经有 8 年历史了(自从它被编写以来,包已经改变了)并用新的包装重写它,当我尝试它时它崩溃了,在 msg.as_string ()。它也是不完整的:例如它创建了一个 ical_atch 但它从未被使用过。我在这里指的是 Auberon Vacher 最赞成的答案
  • 什么是 ics 日历附件?不明白为什么你不能用 django 的本机电子邮件模块做前 2 个。

标签: python django email icalendar


【解决方案1】:

Django有基于python的smtplib的内置解决方案:

https://docs.djangoproject.com/en/3.2/topics/email/

您需要在设置中为您的 smtp 服务器(gmail、mailgun 等)提供一些凭据,然后您才能使用 django.core.mail 模块。

对于附加内容,您可以使用 EmailMessage.attach() 或 attach_file()

https://docs.djangoproject.com/en/3.2/topics/email/#django.core.mail.EmailMessage

【讨论】:

  • “Django 的 send_mail() 和 send_mass_mail() 函数实际上是使用 EmailMessage 类的瘦包装器。”所以为了附加你 100% 需要 EmailMessage 类
  • 来自问题:“”“我已经测试了几个可以附加 ICS 文件的 sn-ps,但是 G-mail 没有让我选择将其添加到日历中,例如它通常会。""" 也许我应该多强调一点。这是关键点。我可以附加一个 ics 文件,但这并不意味着它会在 Gmail 或 Outlook 中正确显示。确切地说,您如何构建电子邮件似乎非常重要。就像您正在使用多方/替代或多部分/混合,并且如果您将正确的标题添加到日历部分。所以理想情况下,我想使用一个确切知道如何做的库。
  • 每个“主要电子邮件客户端”都有自己的 ics 文件链接格式。你可以在这里查看 -> labnol.org/apps/calendar.html '日历链接' 这家伙为每个电子邮件客户端分别生成链接。因此,您想要找到的 90% 的“现成解决方案”并不存在。也许有些客户有自己的库来处理他们的服务(比如 google-> developers.google.com/calendar/api/quickstart/python)但我很确定所有主要客户的解决方案都不存在
  • 感谢@Anton 提供有关创建链接的链接 :) 我会将其保留为备用选项。
【解决方案2】:

首先创建一个.ics文件,当然也可以通过python脚本来生成动态的.ics文件。

.ics 文件

有关iCalendar 的更多信息:基本信息和通用模板(参见下面的示例)。

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//hacksw/handcal//NONSGML v1.0//EN
BEGIN:VEVENT
UID:uid1@example.com
DTSTAMP:19970714T170000Z
ORGANIZER;CN=John Doe:MAILTO:john.doe@example.com
DTSTART:19970714T170000Z
DTEND:19970715T035959Z
SUMMARY:Bastille Day Party
GEO:48.85299;2.36885
END:VEVENT
END:VCALENDAR

如果您觉得这更容易,您也可以generate您自己的 iCalendar(请参阅下面生成的示例)。

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//ical.marudot.com//iCal Event Maker
CALSCALE:GREGORIAN
BEGIN:VTIMEZONE
TZID:Europe/Berlin
LAST-MODIFIED:20201011T015911Z
TZURL:http://tzurl.org/zoneinfo-outlook/Europe/Berlin
X-LIC-LOCATION:Europe/Berlin
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20210716T221958Z
UID:20210716T221958Z-901688629@marudot.com
DTSTART;TZID=Europe/Berlin:20210717T120000
DTEND;TZID=Europe/Berlin:20210717T160000
SUMMARY:Stack Overflow
DESCRIPTION:iCalendar example for Stack Overflow user Ciske\n
LOCATION:Amsterdam
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Stack Overflow
TRIGGER:-PT1H
END:VALARM
END:VEVENT
END:VCALENDAR

Django settings.py

EMAIL_HOST = SMTP server.
EMAIL_HOST_USER = Login credentials for the SMTP server.
EMAIL_HOST_PASSWORD = Password credential for the SMTP server.
EMAIL_PORT = SMTP server port.
EMAIL_USE_TLS or _SSL = True if secure connection.

Django views.py

使用 .ics 文件作为附件发送电子邮件。

from django.core.mail import EmailMessage

# Send email with attachment
email = EmailMessage(
    'Subject',
    'Email body',
    'from@example.com',
    ['to@example.com']
)
email.attach_file('assets/invite.ics', 'text/calendar')
email.send()

您甚至可以将 .html 文件添加到您的电子邮件中。

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

html_content = render_to_string('assets/email.html', context)

email = EmailMultiAlternatives('Subject', 'Email body', 'from@example.com', [to@example.com])
email.attach_alternative(html_content, 'text/html')
email.attach_file('assets/invite.ics', 'text/calendar')
email.send()

【讨论】:

  • 我确定这行不通,因为我已经尝试过了,但是再次尝试,使用您的代码,它可以工作,至少在 Gmail 中,日历附件可以正常显示顶部的约会信息可以通过单击“添加到日历”。我还没有为其他电子邮件客户端测试过这个,但是,由于赏金期已经结束,这是对我想做的最有帮助的答案,赏金是你的。编辑你的 sn-p 和我的区别在于我使用 attach_alternative() 而不是 attach_file() 来邀请。
  • 感谢您的赏金! @Ciske 我很高兴听到它在 Gmail 中有效。让我知道它是否适用于不同的电子邮件客户端。如果没有,我们也许可以想出一个不同的解决方案。
【解决方案3】:

您可以尝试使用mark_safe() 它将js、html 代码作为字符串呈现到html 页面。 我用它来定制 Django admin。 检查下面的示例:

some_sample_string = '''<h1>This is some sample </h1>'''
my_sample_html = mark_safe(some_sample_string)

您可以在 HTML 中设计一个页面,为其添加一些自定义设计,然后将 mark_safe 字节对象返回到 HTML 或任何网页并在那里标记它会起作用。

您可以查看这些链接,这可能对您有所帮助

https://www.kite.com/python/docs/django.utils.safestring.mark_safe

Return mark_safe string from __str__

https://www.fullstackpython.com/django-utils-safestring-mark-safe-examples.html

https://docs.djangoproject.com/en/3.0/_modules/django/utils/html/

【讨论】:

  • 您是否打算将这个答案用于另一个问题?我看不出它与我的问题有什么关系;-p
【解决方案4】:

所以关键是将 ICS 文件附加为文件,而不是字符串(使用 django.core.mail.message.EmailMessage.attach_alternative())。

以下 sn-p 在 Gmail、Hotmail 和 Yahoo 邮件(MS Outlook 待确认)中适用于我,这意味着日历事件信息与电子邮件一起显示,并且至少 Gmail 和 Hotmail 提供了添加选项将事件添加到您的日历中。

from django.core.mail.message import EmailMultiAlternatives  # At the top of your .py file

email = EmailMultiAlternatives(subject, message, settings.FROM_EMAIL, ['recipient@email.here'])
# email.attach_alternative('<b>html here</b>', 'text/html') # Optional HTML message
email.attach_file(filename_event, 'text/calendar')
email.send(fail_silently=False)

我正在使用 ics https://pypi.org/project/ics/ 创建 ICS 文件。该软件包目前仍在维护中。我能找到的唯一其他主要 Python ics 文件库是 ical https://pypi.org/project/icalendar/,截至 2021 年 9 月 1 日,其源代码一年内未更新。

此代码适用于我创建 ics 文件:

from ics import Calendar, Event  # At the top of your .py file

ICS_DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"

calendar = Calendar()
event = Event()
event.name = _("Our event name")
event.begin = appointment.start_time.strftime(ICS_DATETIME_FORMAT)
event.end = appointment.end_time.strftime(ICS_DATETIME_FORMAT)
event.organizer = settings.DEFAULT_FROM_EMAIL
calendar.events.add(event)
filename_event = 'invite-%d.ics' % appointment.id
with open(filename_event, 'w') as ics_file:
    ics_file.writelines(calendar)

其中的约会是我自己的 Django 类,其中 start_time 和 end_time 属于 DateTimeField 类型。

如果您为每个请求创建一个新的 ics 文件,那么每个请求也有一个唯一的文件名很重要,这样您就不会冒两个单独的请求同时写入文件的风险。

发送 ICS 文件后,我会像这样删除它:

import os  # At the top of your .py file

os.remove(filename_event)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-18
    • 2013-11-26
    • 2014-05-12
    • 2011-01-28
    • 2014-11-09
    • 2017-02-04
    • 2012-06-15
    • 2019-11-21
    相关资源
    最近更新 更多