Welcome to Django Mail Templated documentation!

Documentation Status PyPI Package CircleCI Status

Django Mail Templated is a tiny wrapper around the standard EmailMessage class and send_mail() function that provides an easy way to create email messages using the Django template system

The Source code is available at GitHub under MIT license.

Features

  • Built with OOP, KISS and flexibility in mind. Really small and simple, but yet full-featured (I hope).
  • Extends and mimics the built-in Django’s EmailMessage and send_mail(). Compatible as much as possible.
  • Fully supports Django template system including template inheritance (thanks to BradWhittington for the note about the problem).
  • Supports any possible template engines and loaders.
  • Supports serialisation (thanks to arjandepooter).
  • Fully covered with tests.
  • Tested with Django 1.4-1.9.
  • Compatible with Python 3.

Table of Contents

Getting started

Installation

Run:

pip install django-mail-templated

And register the app in your settings file:

INSTALLED_APPS = (
    ...
    'mail_templated'
)

It is also a good practice to ensure that the app is installed successfully and is fully compatible with your environment:

python manage.py test mail_templated

Creating templates

Each email template should extend mail_templated/base.tpl or its clone either directly or via descendants.

Note: newlines at the begin and end of message part blocks will be removed.

Plain text message:

{% extends "mail_templated/base.tpl" %}

{% block subject %}
Hello {{ user.name }}
{% endblock %}

{% block body %}
This is a plain text message.
{% endblock %}

HTML message:

{% extends "mail_templated/base.tpl" %}

{% block subject %}
Hello {{ user.name }}
{% endblock %}

{% block html %}
This is an <strong>html</strong> message.
{% endblock %}

Multipart message:

{% extends "mail_templated/base.tpl" %}

{% block subject %}
Hello {{ user.name }}
{% endblock %}

{% block body %}
This is a plain text part.
{% endblock %}

{% block html %}
This is an <strong>html</strong> part.
{% endblock %}

Sending messages

Fast method using send_mail() function:

from mail_templated import send_mail

send_mail('email/hello.tpl', {'user': user}, from_email, [user.email])

More control with EmailMessage class:

from mail_templated import EmailMessage

message = EmailMessage('email/hello.tpl', {'user': user}, from_email,
                       to=[user.email])
# TODO: Add more useful commands here.
message.send()

Proceed to the Advanced usage section to find more details.

Advanced usage

Plain text and HTML messages

All messages that you create with mail_templated are instances of the mail_templated.EmailMessage class which extends django.core.mail.EmailMultiAlternatives. This does not mean all messages are multipart messages by default, but they become so if both body and html template blocks are not empty in the template rendering output.

{% extends "mail_templated/base.tpl" %}

{% block subject %}
Hello {{ user.name }}
{% endblock %}

{% block body %}
This is a plain text part.
{% endblock %}

{% block html %}
This is an <strong>html</strong> part.
{% endblock %}

In this case the body part goes to the body of the email message, and the html part is attached as alternative.

If you define only one of html and body blocks, it goes to the body of email message with appropriate content type text/plain or text/html.

{% extends "mail_templated/base.tpl" %}

{% block subject %}
Hello {{ user.name }}
{% endblock %}

{% block html %}
This is an <strong>html</strong> message.
{% endblock %}

The template above produces an HTML message without a plain text alternative.

Unused block is empty by default, because it is defined empty in the base template. If you override both blocks but one of them is rendered as empty string, this produces the same result as if the block is not used at all. For example, let’s review this template:

{% extends "mail_templated/base.tpl" %}

{% block subject %}Subject{% endblock %}
{% block body %}{{ plaintext_message }}{% endblock %}
{% block html %}{{ html_message }}{% endblock %}

If the plaintext_message variable is empty then mail_templated will create a message without the plain text part. This way you can be sure the users will not see empty messages. However, only newlines are truncated from the email parts (this is done for convenient template formatting). If the part contains just one space character then it is considered as non-empty.

Default subject and body

Both the mail_templated.send_mail() function and the mail_templated.EmailMessage class got new parameters template_name and context instead of (good) old subject and body. However you can still pass both old parameters as keyword arguments. In this case they will be treated as default values. If there is no appropriate part in the message template (or it is empty) then the default value will be used.

Let’s review this template without subject:

{% extends "mail_templated/base.tpl" %}

{% block body %}
This is a plain text message.
{% endblock %}

Now pass default subject and body to the send_mail() function:

from mail_templated import send_mail

send_mail('email/without_subject.tpl', {},
          'from@inter.net', ['to@inter.net'],
          subject='Default subject', body='Default subject')

The default subject will be used since there is no subject in the template. However the default body will be replaced by the value from the template. The html part also overrides default body if the body part is empty.

Base email template and inheritance

The email template is rendered as a solid document, and all email message parts (subject, body and html) appears concatenated after rendering. For this purpose the base template mail_templated/base.tpl contains special markers for the email message parts, so that they can be found and extracted after rendering.

This approach eliminates the dependency on the inner implementation of the Django template engine. It would be a bad idea to extract and render the blocks objects separately, because the template engine implementation tends to change. But anyway you should not worry about that markup in normal situation. Extend the base template provided by mail_templated and use the template blocks as usually.

You can define your own base template. Just ensure your base template extends the base of the base email templates, and any content is defined inside of blocks subject, body and html.

templates/email/base.html

{% extends "mail_templated/base.tpl" %}

{% block subject %}
{{ COMPANY_NAME }} {% block subject_content %}{% endblock %}
{% endblock %}

{% block html %}
  <img src="{{ COMPANY_LOGO_URL }}" />
  {% block body_content %}
    {% include 'templates/email/parts/standard_greetings.html' %}
  {% endblock %}
{% endblock %}

Now you can extend it as usually:

templates/email/news.html

{% extends "email/base.html" %}

{% block subject_content %}{{ article.title }}{% endblock %}

{% block body_content %}
  {{ block.super }}
  You probably will be interested in this news:
  {{ article.preview }}
{% endblock %}

As you can see, there is nothing special about template inheritance. Actually it is even not required to extend the base of the base email templates. The most base template is just a helper that adds markers to the template. It is better to use it if you want to be sure new version of the mail_templated app will be compatible with your code. But if you want, you can use your own base template with markers for the email parts.

The most base template looks like this:

mail_templated/base.tpl

{{ TAG_START_SUBJECT }}{% block subject %}{% endblock %}{{ TAG_END_SUBJECT }}

{{ TAG_START_BODY }}{% block body %}{% endblock %}{{ TAG_END_BODY }}

{{ TAG_START_HTML }}{% block html %}{% endblock %}{{ TAG_END_HTML }}

Let’s review a simple template as an example:

{% extends "mail_templated/base.tpl" %}

{% block subject %}This is a subject{% endblock %}
{% block body %}This is a plain text body{% endblock %}
{% block html %}This is an html body{% endblock %}

This will compile to:

###start_subject###This is a subject###end_subject###
###start_body###This is a plain text body###end_body###
###start_html###This is an html body###end_html###

You can see that final document contains special tags for the message parts. These markers is the main thing that the base template adds to your message. Instead of extending it, you can use the markers just in your template:

###start_subject###New for {{ week }}###end_subject###
###start_body###
Hellow, {{ username }}! Below is a list of news for {{ week }}.
###end_body###
###start_html###
Hellow, <strong>{{ username }}</strong>!
Below is a list of news for <strong>{{ week }}</strong>.
###end_html###

This is the most efficient and the most inflexible way to define your templates. They will be compiled fast, but there is a chance you will go home much later.

The format of these tags can be changed in settings. The str.format() method is used to format the tags. Please see the Format String Syntax docs if you need more info about formatting.

# Default value is '###{bound}_{block}###'
MAIL_TEMPLATED_TAG_FORMAT='<!--{block}:{bound}-->'
<!--subject:start-->This is a subject<!--subject:end-->
<!--body:start-->This is a plain text body<!--body:end-->
<!--html:start-->This is an html body<!--html:end-->

If there is any probability that the format will change in the future then you probably want to use some variables. mail_templated provides such variable to the context of your templates automatically.

{{ TAG_START_SUBJECT }}This is a subject{{ TAG_END_SUBJECT }}
{{ TAG_START_BODY }}This is a plain text body{{ TAG_END_BODY }}
{{ TAG_START_HTML }}This is an html body{{ TAG_END_HTML }}

You even can change the name format for these variables if the default format conflicts with your code or you just hate it for some personal reason (unfortunately there is no format for the names of these settings, I hope this is not so important really).

# Default value is 'TAG_{BOUND}_{BLOCK}'
MAIL_TEMPLATED_TAG_VAR_FORMAT='{BLOCK}_{BOUND}'
# TODO: Define format for the format of format.
{{ SUBJECT_START }}This is a subject{{ SUBJECT_END }}
{{ BODY_START }}This is a plain text body{{ BODY_END }}
{{ HTML_START }}This is an html body{{ HTML_END }}

Finally you may decide to define your own base template:

{{ SUBJECT_START }}{% block subject %}{% endblock %}{{ SUBJECT_END }}
{{ HTML_START }}{% block body %}{% endblock %}{{ HTML_END }}
{{ BODY_START }}
Please use a modern email client to see the html part of this message.
{{ BODY_END }}

or without these tag name variables:

<!--subject:start-->{% block subject %}{% endblock %}<!--subject:end-->
<!--body:start-->{% block body %}{% endblock %}<!--body:end-->
<!--html:start-->
Please use a modern email client to see the html part of this message.
<!--html:end-->

Don’t forget to add a test that checks the mail_templated app with your format of templates. Something like this would be fine:

from django.core import mail
from django.test import TestCase

from mail_templated import send_mail


class SendMailTestCase(TestCase):

    def test_plain(self):
        send_mail('email/test.tpl', {'name': 'User'},
                  'from@inter.net', ['to@inter.net'])
        self.assertEqual(len(mail.outbox), 1)
        message = mail.outbox[0]
        self.assertEqual(message.from_email, 'from@inter.net')
        self.assertEqual(message.to, ['to@inter.net'])
        self.assertEqual(message.subject, 'Message for User')
        self.assertEqual(message.body, 'Hello, User!')

Working with send_mail() function

You probably know that the API for Django's send_mail() function from django.core.mail is frozen. Any new code wanting to extend the functionality goes to the django.core.mail.EmailMessage class. The mail_templated.send_mail() function works almost exactly the same way as the standard one. But it is much more powerful than it seems at the first look. The magic is done by passing all extra keyword arguments to the mail_templated.EmailMessage class constructor, which then passes them to the base class django.core.mail.EmailMultiAlternatives. Thus you can use all those features that are accessible via parameters of the EmailMessage class constructor.

For example, you can add attachments like in this example:

send_mail(
    'email/message.tpl', context_dict, from_email, [email],
    attachments=[('attachment.png', content, 'image/png')])

The limitation of this feature is that you can’t attach a file from the file system. But if the content is in the variable already then this will work well for you.

You can attach alternatives the same way:

send_mail(
    'email/message.tpl', context_dict, from_email, [email],
    alternatives=[('HTML alternative', 'text/html')])

You can also specify cc, bcc, reply_to and extra headers. Please review the API documentations for detailed info about parameters:

Working with EmailMessage class

The mail_templated.EmailMessage class supports all the features that are supported by the django.core.mail.EmailMultiAlternatives class. And of course it provides ability to use templates. If you have a complex task that can not be done in one step then this class is probably what you need. In other case consider the send_mail() function.

The message instance may be initialized with many various parameters. The most common case will probably look like this:

message = EmailMessage('email/message.tpl', context, from_email, [email])

But you are free to create completely empty message and initialize it later.

message = EmailMessage()
message.template_name = 'email/message.tpl'
message.context = {'user_names': []}
message.from_email = from_email
message.to = []
for user in users:
    message.context['user_names'].append(user)
    message.to.append(user.email)

The EmailMessage class has all methods that are available in the base classes, so you can use this class in the usual way.

message.attach_alternative(html_content, 'text/html')
message.attach_file(image_file_name, 'image/png')

Finally just send the message when you are done.

message.send()

As you can see this is almost regular email message object. You just set template_name and context instead of subject and body, and all the work is done behind the scene. But in fact you have more control that you can use when needed. This will be described in the next sections.

Please review the API documentations for detailed info about parameters and attributes:

Loading and rendering the email template

The template that you specify via template_name on the EmailMessage class initialization is loaded and rendered automatically when you call the send() method. It tries to do this as late as possible. But you can take the control and force this at any time. The EmailMessage class provides two methods for this needs: load_template() and render().

The most fragmented approach looks like this:

message = EmailMessage()
message.template_name = 'email/message.tpl'
message.load_template()
message.render()
message.send()

You can pass the template name either to the constructor or to the load_template() method:

message = EmailMessage('email/message.tpl')
message.load_template()

message = EmailMessage()
message.load_template('email/message.tpl')

You even can load it manually and then set via the template property.

message = EmailMessage()
message.template = get_template('email/message.tpl')

And you even can omit the call to the load_template() method and just use the render() method only. When you try to render the template in any way, it will be loaded automatically if not loaded yet.

Before you render the template, a context dict should be provided. There are also few variants how you can do this.

message = EmailMessage('email/message.tpl', context)
message.render()

message = EmailMessage('email/message.tpl')
message.context = context
message.render()

message = EmailMessage('email/message.tpl')
message.render(context)

Finally you can pass render=True to the constructor if you want to render it immediately.

message = EmailMessage('email/message.tpl', context, render=True)

There is also nothing wrong (expect of efficiency) if you want to load and render one template, then load and render another one.

message = EmailMessage(customer.message_template_file, context,
                       from_email, [email])
message.render()
if not is_valid_html(message.body):
    message.load_template('email/fallback_message.tpl')
    message.context.update(fallback_extra_context)
    message.render()
message.send()

As you can see in this example, you can access the resulting subject and body as soon as the message is rendered.

message = EmailMessage('email/message.tpl', context, render=True)
logger.debug('Subject: ' + message.subject)
logger.debug('Body: ' + message.body)
if message.alternatives:
    logger.debug('Alternarive: ' + message.alternatives[0][0])

When rendered, the message object becomes very similar to the standard Django’s EmailMessage class. You can check current status via the is_rendered property.

Serialization

mail_templated supports the pickle module. You can serialize the message object at any stage. However what really makes sense is serializing before invoking the load_template() method or after invoking the render() method. If you decide to serialize just between the calls to these methods then you will lost the compiled template instance, because it can not be serialized with pickle.

Let’s play with the email object a little.

>>> import pickle
>>> from mail_templated import EmailMessage
>>> message = EmailMessage('email/message.tpl', {})

As soon as the message exists, you can serialize it.

>>> # Serialize the message.
>>> pickled_message = pickle.dumps(message)
>>> # Let's see how it looks now.
>>> print repr(pickled_message)
"ccopy_reg\n_reconstructor\np0\n(cmail_templated.message\nEmailMessage\np1\
nc__builtin__\nobject\np2\nNtp3\nRp4\n(dp5\nS'body'\np6\nNsS'extra_headers'
\np7\n(dp8\nsS'attachments'\np9\n(lp10\nsS'_is_rendered'\np11\nI00\nsS'cc'\
np12\n(lp13\nsS'template_name'\np14\nS'mail_templated_test/plain.tpl'\np15\
nsS'alternatives'\np16\n(lp17\nsS'bcc'\np18\n(lp19\nsS'to'\np20\n(lp21\nsS'
connection'\np22\nNsS'context'\np23\n(dp24\nsS'reply_to'\np25\n(lp26\nsS'fr
om_email'\np27\nS'webmaster@localhost'\np28\nsS'subject'\np29\nNsb."

Now you can store it somewhere for later use. Then load and de-serialize the message when needed, and it is ready for further processing.

>>> # De-serialize the message.
>>> message2 = pickle.loads(pickled_message)
>>> # Check the message state.
>>> print repr(message2)
<mail_templated.message.EmailMessage object at 0x7ffb8ad2e810>
>>> print repr(message2.template)
None
>>> # The template is not loaded yet. Load the template
>>> message2.load_template()
>>> # How is it now?
>>> print repr(message2.template)
<django.template.backends.django.Template object at 0x7ffb8a11c050>
>>> # Good! It's ready for rendering.

While the template is loaded, let’s try to serialize and de-serialize it again.

>>> # Serialize/de-serialize again.
>>> message3 = pickle.loads(pickle.dumps(message2))
>>> # Is the message still alive?
>>> print repr(message3)
<mail_templated.message.EmailMessage object at 0x7ffb8ad4f790>
>>> # Yes, it's still alive, that's good. What about the template?
>>> print repr(message3.template)
None
>>> # Ooops! We lost the template object. So now we have to load it again.
>>> message3.load_template()
>>> print repr(message3.template)
<django.template.backends.django.Template object at 0x7ffb8a0fdf10>
>>> # Phew! It's here now.

Actually if lost, the template will be loaded automatically again when you try to render it. You will not see any errors. Just your code will do some useless extra work.

>>> message4 = pickle.loads(pickle.dumps(message3))
>>> print repr(message4.template)
None
>>> # Oh no! We lost it again :(
>>> message4.render()
>>> # Hmm... There is no any error!
>>> print repr(message4.template)
<django.template.backends.django.Template object at 0x7ffb8a0b4d50>
>>> # Magic? No, this is by design!

So, remember to load the template just before the rendering, not before serialization.

Once rendered, you can serialize/de-serialize it again without problems.

>>> # Check the message state.
>>> print repr([message4.is_rendered, message4.subject, message4.body])
[True, u'Test subject', u'Test body']
>>> # Continue the tortures.
>>> message5 = pickle.loads(pickle.dumps(message4))
>>> # The author said it should work fine now.
>>> print repr(message5.template)
None
>>> # :`(
>>> # :```(
>>> # But wait!
>>> print repr([message5.is_rendered, message5.subject, message5.body])
[True, u'Test subject', u'Test body']
>>> # Heh, the template is not needed anymore! :D

There are so many combination how you can load, render and serialize the message, so that I’m afraid I can’t describe all of them here. These examples should help you to construct your own combination.

Cleanup for third-party libraries

There are many third-party libraries that help you to work with email messages. If a library can work with the standard django.core.mail.EmailMessage class then it probably can work without problems with mail_templated.EmailMessage. However some library may be surprised by the additional attributes on the email message object. For example, the Djrill app will pass your template_name to the Mandrill service because it provides it’s own template system, and it uses the template_name parameter too (what a surprise!).

If something similar happens to your messages then you should wipe out the tracks of the mail_templated app. The most easy way to do this is to delete the conflicting attributes. The EmailMessage class provides a convenient method clean() for this purpose. It removes the most expensive and risky properties - context, template and template_name.

If you use the send_mail() function then the cleanup is invoked for you automatically just after rendering. You can disable this behaviour by passing the clean=False keyword argument.

If you use the EmailMessage class then you should care of cleanup yourself. Fortunately there are many places where you can invoke the clean() method either directly or via special keyword argument clean.

# Invoke the cleanup right on the initialisation.
message = EmailMessage('email/message.tpl', {}, render=True, clean=True)
# Call the method manually after rendering.
message.render()
message.clean()
# Pass `clean=True` to the `render()` method.
message.render(clean=True)
# The `send()` method also supports this argument.
message.send(clean=True)

There is no much difference in these variants. Just choice one that makes your code clean and clear.

API Reference

Like the standard django.core.mail module, mail_templated provides two options to send an email message:

send_mail()

mail_templated.send_mail(template_name, context, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None, connection=None, **kwargs)[source]

Easy wrapper for sending a single email message to a recipient list using django template system.

It works almost the same way as the standard send_mail() function.

The main difference is that two first arguments subject and body are replaced with template_name and context. However you still can pass subject or body as keyword arguments to provide static content if needed.

The template_name, context, from_email and recipient_list parameters are required.

Note

The set of possible parameters is not limited by the list below. Any additional parameters are passed to the constructor of EmailMultiAlternatives class.

Parameters:
  • template_name (str) – The template name that extends mail_templated/base.tpl with (optional) blocks {% subject %}, {% body %} and {% html %}.
  • context (dict) – A dictionary to be used as a context for template rendering.
  • from_email (str) – The email address for the “From:” field.
  • recipient_list (list) – The recipient email addresses. Each member of this list will see the other recipients in the “To:” field of the email message.
Keyword Arguments:
 
  • fail_silently (bool) – If it’s False, send_mail will raise an smtplib.SMTPException. See the smtplib docs for a list of possible exceptions, all of which are subclasses of smtplib.SMTPException.
  • auth_user | str – The optional username to use to authenticate to the SMTP server. If this isn’t provided, Django will use the value of the EMAIL_HOST_USER setting.
  • auth_password | str – The optional password to use to authenticate to the SMTP server. If this isn’t provided, Django will use the value of the EMAIL_HOST_PASSWORD setting.
  • connection (EmailBackend) – The optional email backend to use to send the mail. If unspecified, an instance of the default backend will be used. See the documentation on Email backends for more details.
  • subject (str) – Default message subject. Used if {% subject %} block is empty or does not exist in the specified email template.
  • body (str) – Default message body. Used if {% body %} block is empty or does not exist in the specified email template.
  • render (bool) – If True, render template and set subject, body and html properties immediately. Default is False.
Returns:

The number of successfully delivered messages (which can be 0 or 1 since it can only send one message).

Return type:

int

See also

django.core.mail.send_mail()
Documentation for the standard send_mail() function.

EmailMessage

class mail_templated.EmailMessage(template_name=None, context={}, *args, **kwargs)[source]

Extends standard EmailMultiAlternatives class with ability to use templates

See also

django.core.mail.EmailMessage
Documentation for the standard email message classes.
__init__(template_name=None, context={}, *args, **kwargs)[source]

Initialize single templated email message (which can be sent to multiple recipients).

When using with a user-specific message template for mass mailing, create new EmailMessage object for each user. Think about this class instance like about a single paper letter (you would not reuse it, right?).

The class tries to provide interface as close to the standard Django classes as possible. The main difference is that two first arguments subject and body are replaced with template_name and context. However you still can pass subject or body as keyword arguments to provide static content if needed.

All parameters are optional and can be set at any time prior to calling the render() and send() methods.

Note

The set of possible parameters is not limited by the list below. Any additional parameters are passed to the constructor of EmailMultiAlternatives class.

Parameters:
  • template_name (str) – The template name that extends mail_templated/base.tpl with (optional) blocks {% subject %}, {% body %} and {% html %}.
  • context (dict) – A dictionary to be used as a context for template rendering.
  • from_email (str) – The email address for the “From:” field.
  • recipient_list (list) – The recipient email addresses. Each member of this list will see the other recipients in the “To:” field of the email message.
Keyword Arguments:
 
  • subject (str) – Default message subject. Used if {% subject %} block is empty or does not exist in the specified email template.
  • body (str) – Default message body. Used if {% body %} block is empty or does not exist in the specified email template.
  • render (bool) – If True, render template and set subject, body and html properties immediately. Default is False.
  • clean (bool) – If True, remove any template specific properties from the message object. This may be useful if you pass render=True. Default is False.
attach(filename=None, content=None, mimetype=None)[source]

Attaches a file with the given filename and content. The filename can be omitted and the mimetype is guessed, if not provided.

If the first parameter is a MIMEBase subclass it is inserted directly into the resulting message attachments.

attach_alternative(content, mimetype)

Attach an alternative content representation.

attach_file(path, mimetype=None)[source]

Attaches a file from the filesystem.

The mimetype will be set to the DEFAULT_ATTACHMENT_MIME_TYPE if it is not specified and cannot be guessed or (PY3 only) if it suggests text/* for a binary file.

clean()[source]

Remove any template specific properties from the message object.

Useful if you want to serialize rendered message without template-specific properties. Also allows to avoid conflicts with Djrill/Mandrill and other third-party systems that may fail because of non-standard properties of the message object.

The messages should be rendered already, or you will have to setup the context and template/template_name after deserialization.

In most cases you can pass the clean parameter to the constructor or another appropriate method of this class.

load_template(template_name=None)[source]

Load a template by it’s name using the current template loaders.

Parameters:template_name (str) – The template name that extends mail_templated/base.tpl with (optional) blocks {% subject %}, {% body %} and {% html %}. If not specified then the template_name property is used.
recipients()[source]

Returns a list of all recipients of the email (includes direct addressees as well as Cc and Bcc entries).

render(context=None, clean=False)[source]

Render email with provided context

Parameters:context (dict) – A dictionary to be used as a context for template rendering. If not specified then the context property is used.
Keyword Arguments:
 clean (bool) – If True, remove any template specific properties from the message object. Default is False.
send(*args, **kwargs)[source]

Send email message, render if it is not rendered yet.

Note

Any extra arguments are passed to EmailMultiAlternatives.send().

Keyword Arguments:
 clean (bool) – If True, remove any template specific properties from the message object. Default is False.

Troubleshooting

If the app does not work as expected please follow the following steps:

  1. Update to the latest version:

    pip install -U django-mail-templated
    
  2. Run tests within your current Django project environment:

    python manage.py test mail_templated
    
  3. Run tests in a standalone mode:

    python -m mail_templated.tests.run
    
  4. Create a GitHub issue.

You are also very welcome to try fixing the problem by yourself:

  1. Fork and clone the GitHub repository.
  2. Add a test case that demonstrates the problem.
  3. Fix it and create a pull request.

Changelog

2.6.x

  • Fixed a bug with default body and html message.
  • Added context parameter to the render() method.
  • Made the template_name parameter not required for the load_template() method.
  • Removed *args from the send_mail() function.
  • Disabled HTML escape in the text parts.

2.5.x

  • Added application settings.
  • Added ability to cleanup the message object (enabled in send_mail() by default).

2.4.x

  • Fixed setup.py, added missing files to the package.
  • Added more tests.

2.3.x

  • Made template_name argument required when render=True passed to __init__().
  • Removed argument render of method send().
  • Added public property is_rendered.
  • Added more tests.

2.2.x

  • Fixed compatibility with Python 3

2.1.x

  • Added full support of template inheritance.
  • Added obligatory base email template.
  • Fixed broken pickling/unpickling feature.
  • render=True is now ignored on initialisation without template. Raised error before.

2.0.x

This is intermediate version with broken pickling/unpickling feature. Using of next or previous version is highly recommended.

  • Slightly changed the list of arguments for EmailMessage.__init__().
  • Replaced template and template_name property setters with method load_template().
  • Added method render().
  • Added support for late initialisation.
  • Improved tests.

1.0.x

  • Fixed the multilingual templates support for Django >= 1.8.

Contributors

Author:

Artem Rizhov

Other contributors:

Special thanks to Thomas Parslow