I want to send HTML-emails, using Django templates like this:
<html>
<body>
hello <strong>{{username}}</strong>
your account activated.
<img src="mysite.com/logo.gif" />
</body>
I can't find anything about send_mail
, and django-mailer only sends HTML templates, without dynamic data.
How do I use Django's template engine to generate e-mails?
From the docs, to send HTML e-mail you want to use alternative content-types, like this:
from django.core.mail import EmailMultiAlternatives
subject, from_email, to = 'hello', 'from@example.com', 'to@example.com'
text_content = 'This is an important message.'
html_content = '<p>This is an <strong>important</strong> message.</p>'
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
msg.attach_alternative(html_content, "text/html")
msg.send()
You'll probably want two templates for your e-mail - a plain text one that looks something like this, stored in your templates directory under email.txt
:
Hello {{ username }} - your account is activated.
and an HTMLy one, stored under email.html
:
Hello <strong>{{ username }}</strong> - your account is activated.
You can then send an e-mail using both those templates by making use of get_template
, like this:
from django.core.mail import EmailMultiAlternatives
from django.template.loader import get_template
from django.template import Context
plaintext = get_template('email.txt')
htmly = get_template('email.html')
d = Context({ 'username': username })
subject, from_email, to = 'hello', 'from@example.com', 'to@example.com'
text_content = plaintext.render(d)
html_content = htmly.render(d)
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
msg.attach_alternative(html_content, "text/html")
msg.send()
Since Django's 1.7 in send_email method the html_message
parameter was added.
html_message: If html_message is provided, the resulting email will be a multipart/alternative email with message as the text/plain content type and html_message as the text/html content type.
So you can just:
from django.core.mail import send_mail
from django.template.loader import render_to_string
msg_plain = render_to_string('templates/email.txt', {'some_params': some_params})
msg_html = render_to_string('templates/email.html', {'some_params': some_params})
send_mail(
'email title',
msg_plain,
'some@sender.com',
['some@receiver.com'],
html_message=msg_html,
)
send_mail
is not possible to set some custom header like i.e Return-Path
which can be set with the EmailMultiAlternatives's constructor header parameter
django.template.exceptions.TemplateDoesNotExist
error. by default path is relative to the templates folder
I have made django-templated-email in an effort to solve this problem, inspired by this solution (and the need to, at some point, switch from using django templates to using a mailchimp etc. set of templates for transactional, templated emails for my own project). It is still a work-in-progress though, but for the example above, you would do:
from templated_email import send_templated_mail
send_templated_mail(
'email',
'from@example.com',
['to@example.com'],
{ 'username':username }
)
With the addition of the following to settings.py (to complete the example):
TEMPLATED_EMAIL_DJANGO_SUBJECTS = {'email':'hello',}
This will automatically look for templates named 'templated_email/email.txt' and 'templated_email/email.html' for the plain and html parts respectively, in the normal django template dirs/loaders (complaining if it cannot find at least one of those).
django.shortcuts.send_templated_mail
: code.djangoproject.com/ticket/17193
I know this is an old question, but I also know that some people are just like me and are always looking for uptodate answers, since old answers can sometimes have deprecated information if not updated.
Its now January 2020, and I am using Django 2.2.6 and Python 3.7
Note: I use DJANGO REST FRAMEWORK, the code below for sending email was in a model viewset in my views.py
So after reading multiple nice answers, this is what I did.
from django.template.loader import render_to_string
from django.core.mail import EmailMultiAlternatives
def send_receipt_to_email(self, request):
emailSubject = "Subject"
emailOfSender = "email@domain.com"
emailOfRecipient = 'xyz@domain.com'
context = ({"name": "Gilbert"}) #Note I used a normal tuple instead of Context({"username": "Gilbert"}) because Context is deprecated. When I used Context, I got an error > TypeError: context must be a dict rather than Context
text_content = render_to_string('receipt_email.txt', context, request=request)
html_content = render_to_string('receipt_email.html', context, request=request)
try:
#I used EmailMultiAlternatives because I wanted to send both text and html
emailMessage = EmailMultiAlternatives(subject=emailSubject, body=text_content, from_email=emailOfSender, to=[emailOfRecipient,], reply_to=[emailOfSender,])
emailMessage.attach_alternative(html_content, "text/html")
emailMessage.send(fail_silently=False)
except SMTPException as e:
print('There was an error sending an email: ', e)
error = {'message': ",".join(e.args) if len(e.args) > 0 else 'Unknown Error'}
raise serializers.ValidationError(error)
Important! So how does render_to_string
get receipt_email.txt
and receipt_email.html
? In my settings.py
, I have TEMPLATES
and below is how it looks
Pay attention to DIRS
, there is this line os.path.join(BASE_DIR, 'templates', 'email_templates')
.This line is what makes my templates accessible. In my project_dir, I have a folder called templates
, and a sub_directory called email_templates
like this project_dir->templates->email_templates
. My templates receipt_email.txt
and receipt_email.html
are under the email_templates
sub_directory.
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates'), os.path.join(BASE_DIR, 'templates', 'email_templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
Let me just add that, my recept_email.txt
looks like this;
Dear {{name}},
Here is the text version of the email from template
And, my receipt_email.html
looks like this;
Dear {{name}},
<h1>Now here is the html version of the email from the template</h1>
Use EmailMultiAlternatives and render_to_string to make use of two alternative templates (one in plain text and one in html):
from django.core.mail import EmailMultiAlternatives
from django.template import Context
from django.template.loader import render_to_string
c = Context({'username': username})
text_content = render_to_string('mail/email.txt', c)
html_content = render_to_string('mail/email.html', c)
email = EmailMultiAlternatives('Subject', text_content)
email.attach_alternative(html_content, "text/html")
email.to = ['to@example.com']
email.send()
I have created Django Simple Mail to have a simple, customizable and reusable template for every transactional email you would like to send.
Emails contents and templates can be edited directly from django's admin.
With your example, you would register your email :
from simple_mail.mailer import BaseSimpleMail, simple_mailer
class WelcomeMail(BaseSimpleMail):
email_key = 'welcome'
def set_context(self, user_id, welcome_link):
user = User.objects.get(id=user_id)
return {
'user': user,
'welcome_link': welcome_link
}
simple_mailer.register(WelcomeMail)
And send it this way :
welcome_mail = WelcomeMail()
welcome_mail.set_context(user_id, welcome_link)
welcome_mail.send(to, from_email=None, bcc=[], connection=None, attachments=[],
headers={}, cc=[], reply_to=[], fail_silently=False)
I would love to get any feedback.
There is an error in the example.... if you use it as written, the following error occurs:
< type 'exceptions.Exception' >: 'dict' object has no attribute 'render_context'
You will need to add the following import:
from django.template import Context
and change the dictionary to be:
d = Context({ 'username': username })
See http://docs.djangoproject.com/en/1.2/ref/templates/api/#rendering-a-context
Django Mail Templated is a feature-rich Django application to send emails with Django template system.
Installation:
pip install django-mail-templated
Configuration:
INSTALLED_APPS = (
...
'mail_templated'
)
Template:
{% block subject %}
Hello {{ user.name }}
{% endblock %}
{% block body %}
{{ user.name }}, this is the plain text part.
{% endblock %}
Python:
from mail_templated import send_mail
send_mail('email/hello.tpl', {'user': user}, from_email, [user.email])
More info: https://github.com/artemrizhov/django-mail-templated
send_emai()
didn't work for me so I used EmailMessage
here in django docs.
I have included two versions of the anser:
With html email version only With plain text email and html email versions
from django.template.loader import render_to_string
from django.core.mail import EmailMessage
# import file with html content
html_version = 'path/to/html_version.html'
html_message = render_to_string(html_version, { 'context': context, })
message = EmailMessage(subject, html_message, from_email, [to_email])
message.content_subtype = 'html' # this is required because there is no plain text email version
message.send()
If you want to include a plain text version of your email, modify the above like this:
from django.template.loader import render_to_string
from django.core.mail import EmailMultiAlternatives # <= EmailMultiAlternatives instead of EmailMessage
plain_version = 'path/to/plain_version.html' # import plain version. No html content
html_version = 'path/to/html_version.html' # import html version. Has html content
plain_message = render_to_string(plain_version, { 'context': context, })
html_message = render_to_string(html_version, { 'context': context, })
message = EmailMultiAlternatives(subject, plain_message, from_email, [to_email])
message.attach_alternative(html_message, "text/html") # attach html version
message.send()
My plain and html versions look like this: plain_version.html:
Plain text {{ context }}
html_version.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
...
</head>
<body>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="320" style="border: none; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; line-height: 1.5;">
...
{{ context }}
...
</table>
</body>
</html>
txt
file so I've used the EmailMessage
class. Thanks for this awesome solution mate! :-)
I like using this tool to permit easily to send email HTML and TXT with easy context processing: https://github.com/divio/django-emailit
I wrote a snippet that allows you to send emails rendered with templates stored in the database. An example:
EmailTemplate.send('expense_notification_to_admin', {
# context object that email template will be rendered with
'expense': expense_request,
})
If you want dynamic email templates for your mail then save the email content in your database tables. This is what i saved as HTML code in database =
<p>Hello.. {{ first_name }} {{ last_name }}. <br> This is an <strong>important</strong> {{ message }}
<br> <b> By Admin.</b>
<p style='color:red'> Good Day </p>
In your views:
from django.core.mail import EmailMultiAlternatives
from django.template.loader import get_template
def dynamic_email(request):
application_obj = AppDetails.objects.get(id=1)
subject = 'First Interview Call'
email = request.user.email
to_email = application_obj.email
message = application_obj.message
text_content = 'This is an important message.'
d = {'first_name': application_obj.first_name,'message':message}
htmly = FirstInterviewCall.objects.get(id=1).html_content #this is what i have saved previously in database which i have to send as Email template as mentioned above HTML code
open("partner/templates/first_interview.html", "w").close() # this is the path of my file partner is the app, Here i am clearing the file content. If file not found it will create one on given path.
text_file = open("partner/templates/first_interview.html", "w") # opening my file
text_file.write(htmly) #putting HTML content in file which i saved in DB
text_file.close() #file close
htmly = get_template('first_interview.html')
html_content = htmly.render(d)
msg = EmailMultiAlternatives(subject, text_content, email, [to_email])
msg.attach_alternative(html_content, "text/html")
msg.send()
This will send the dynamic HTML template what you have save in Db.
Success story sharing
plaintext
andhtmly
, and just set templates and contexts when you definetext_content
andhtml_content
.3.1.5
with python3.8.5
. No need tofrom django.template import Context
. Just dod = { 'username': username }
.