I've been searching for a library or at least functional snippet of code that lets me send an e-mail from Django (or at least in Python) with text content, HTML content, and an ICS calendar attachment that is recognized by every major e-mail client. For my particular use case it's enough if the user is offered an 'add to calendar' button.
I feel like this should be a solved problem by now, but I'm only finding answers that refer to libraries that aren't being maintained or that are outdated or incomplete in some other way. I've tested a couple of snippets that will attach an ICS file, but G-mail doesn't give me the option of adding it to the calendar like it usually does.
Is there a ready made solution that I'm missing?
So the key was to attach the ICS file as a file, not as a string (using django.core.mail.message.EmailMessage.attach_alternative()
).
The following snippet works for me in Gmail, Hotmail, and Yahoo mail (MS Outlook to be confirmed), meaning the calendar event information is shown together with the email, and at least Gmail and Hotmail provide an option to add the event to your calendar.
from django.core.mail.message import EmailMultiAlternatives # At the top of your .py file
email = EmailMultiAlternatives(subject, message, settings.FROM_EMAIL, ['[email protected]'])
# email.attach_alternative('<b>html here</b>', 'text/html') # Optional HTML message
email.attach_file(filename_event, 'text/calendar')
email.send(fail_silently=False)
I'm using ics
https://pypi.org/project/ics/ to create the ICS file. This package is currently still being maintained. The only other major Python ics file library I could find is ical
https://pypi.org/project/icalendar/ and the source for that hasn't been updated in a year as of sep 1st, 2021.
This code works for me to create the ics file:
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)
where appointment is my own Django class, of which start_time and end_time are of the type DateTimeField.
If you create a new ics file per request, it's important to also have a unique filename per request, so you don't risk two separate requests writing to the file simultaneously.
After sending the ICS file I'll delete it like so:
import os # At the top of your .py file
os.remove(filename_event)