4. Usage¶
4.1. mail.send()¶
mail.send() is one of the most important function in this library.
It is used to send one email to a list of recipients. It takes these arguments:
Argument |
Type |
Required |
Description |
|---|---|---|---|
recipients |
str | List[str | EmailAddress] |
Yes |
List of recipient email addresses |
sender |
str |
No |
Defaults to |
subject |
str (context vars allowed) |
No |
Subject of Email, if |
message |
str (context vars allowed) |
No |
Content of Email, if |
html_message |
str (context vars allowed) |
No |
HTML content of Email, if |
emailmerge |
str | EmailMerge |
No |
EmailMerge instance or name |
language |
str |
No |
Language (code) in which you want to send email. Defaults to |
cc |
List[str | EmailAddress] |
No |
List of emails in cc(Carbon copy) field |
bcc |
List[str | EmailAddress] |
No |
List of emails in bcc(Blind carbon copy) field |
attachments |
dict |
No |
Email attachments - a dict where the keys are the filenames and the values are files, file-like-objects or path to file |
context |
dict |
No |
Dictionary where keys are strings and values are any serializable objects. Used to render email |
headers |
dict |
No |
Extra headers on the message |
scheduled_time |
datetime | date |
No |
Indicates when the message should be sent |
expires_at |
datetime | date |
No |
If specified, mails that are not yet sent won’t be delivered after this date. |
expires_at |
datetime | date |
No |
If specified, mails that are not yet sent won’t be delivered after this date. |
priority |
str |
No |
|
backend |
str |
No |
Alias of the backend you want to use from |
Note: Some arguments can take strings with special variables allowed.
Examples of special variables include: #var1#, #recipient.first_name#, etc.
from sendmail import mail
mail.send(
[EmailAddress.objects.create(email='peter@gmail.com', first_name='Peter'), 'lena@email.com', 'ben@yahoo.com'],
'from@example.com',
subject='My email',
message='Hi there!',
html_message='Hi <strong>#where#</strong>!',
context={'where': 'there'},
cc=['cc1@email.com'],
bcc=['bcc1@email.com', 'bcc2@email.com']
)
The command above will queue 1 email specified lists of recipients. HTML message will be equal to Hi there!
Passing now as the priority allows to bypass the queue and deliver the email right away.
from sendmail import mail
mail.send(
'recipient@example.com', # List of email addresses or list of EmailAddress also accepted
'from@example.com',
emailmerge='your-template-here', # Could be an EmailMergeModel instance or name
context={'generator': 'sendmail',
'username': 'michaelpoi',}, # Context is used to fill both {{ var }} in html and #var# in ckeditor.
language='en', # If not specified settings.LANGUAGE_CODE is used,
priority='now'
)
4.2. EmailAddress and recipient context¶
In the sendmail recipients are stored as EmailAddress model instances. This was done to allow personalization of emails. EmailAddress model has the following attributes:
Attribute |
Type |
Req. |
Signature text |
Signature html |
Description |
|---|---|---|---|---|---|
str |
Yes |
#recipient.email# |
{{ recipient.email }} |
Recipient email address, can also be a display name (Johh <johh@email.com>) |
|
first_name |
str |
No |
#recipient.first_name# |
{{ recipient.first_name }} |
Recipient first name. |
last_name |
str |
No |
#recipient.last_name# |
{{ recipient.last_name }} |
Recipient surname. |
gender |
str |
No |
#recipient.gender# |
{{ recipient.gender }} |
Recipient gender. Can be |
preferred_language |
str |
No |
- |
{{ recipient.preferred_language }} |
Recipient preferred_language. If using mail.send_many() without language argument email to a certain user will be translated.
If specified here language is not in |
is_blocked |
bool |
No |
- |
- |
Defaults to False. If set to True recipient wont get any emails, no matter with mail.send() or mail.send_many() |
Every time you use mail.send() or mail.send_many() list of recipients and cc or bcc (only for mail.send() ) are transformed to a list
of EmailAddress instances. If recipient is in database it just selects it by email, otherwise creates a new instance with None for
all non-required fields.
Recipient context is always passed to extend email context, however:
If you use mail.send() only 1 email is generated, so the context for the first recipient in a list is used to render email.
If you use mail.send_many() recipient context is passed to all emails generated.
Recipient context can be used in all phases of template creation (see more Templating). For example you can add to html template something like this:
{% with gender=recipient.gender %}
{% if gender == 'male' %}
Mr.
{% elif gender == 'female' %}
Ms.
{% else %}
Human
{% endif %}
{% endwith %}
{{ recipient.first_name }} {{ recipient.last_name }}
This way you can achieve personalized greeting for each recipient when using mail.send_many().
You can use this context when filling subject, content or placeholders values in CKEditor fields as well. For example:
from sendmail import mail
from sendmail.models import EmailAddress
john = EmailAddress.objects.create(email='john.doe@email.com',
first_name='John',
last_name='Doe')
mail.send(
'john.doe@email.com',
'from@example.com',
subject='Message for #recipient.first_name#',
html_message = '<h1>#recipient.first_name# #recipient.last_name#</h1>'
)
4.2.1. Overriding EmailAddress¶
If you want to add your custom fields to EmailAddress and use it in recipients context for email personalization, you can define your own swapping model based on EmailAddress:
In models.py of your app:
from sendmail.models.base import AbstractEmailAddress
from django.db import models
from sendmail.mixins import SwappableMetaMixin
class CustomEmailAddress(AbstractEmailAddress, SwappableMetaMixin):
phone_number = models.CharField(max_length=20, blank=True, null=True)
# Any custom fields
Note that your app has to be listed in INSTALLED_APPS.
Then, you need to specify your custom model using EMAIL_ADDRESS_MODEL setting in settings.py
EMAIL_ADDRESS_MODEL = 'custom_user.CustomEmailAddress'
The default value is sendmail.EmailAddress.
Now if you restart your server you will be able to see updated model in admin interface and fill it with your data. Also, your entered data will be passed to templates and can be used like {{ recipient.field_name }} in html and #recipient.field_name# in rich contents.
If you have to get active model class:
from sendmail.settings import get_email_address_model
EmailAddress = get_email_address_model()
print(EmailAddress.objects.first()) # Use your model
Warning
It is not recommended to swap models in an operational database, as doing so may lead to inconsistencies, particularly in ForeignKey relationships. Swapping models can break existing references and constraints, resulting in data integrity issues. Always ensure that appropriate migrations and data adjustments are in place before making such changes.
4.3. mail.send_many()¶
mail.send_many() is one of the most important function in the library. It is used to generate n (number of recipients)
emails (one for each recipient in recipients).
mail.send_many() is much more efficient alternative for mail.send() , because it utilizes much less database queries.
Using mail.send_many() you can maximize personalization like discussed in section above.
mail.send_many() takes the same set of parameters like mail.send() , except:
ccandbcccan not be used inmail.send_many()prioritycan not benow
Other parameters are shared among generated emails.
import tempfile
from sendmail import mail
from sendmail.models import EmailAddress
lena = EmailAddress.objects.create(email='lena@email.com', first_name='Lena')
ben = EmailAddress.objects.create(email='ben@yahoo.com', first_name='Ben', is_blocked=True)
with tempfile.NamedTemporaryFile(delete=True) as f:
f.write(b'Testing attachments')
f.seek(0)
mail.send_many(
recipients=[EmailAddress.objects.create(email='bob@gmail.com', first_name='Bob'), 'lena@email.com', 'ben@yahoo.com'],
sender='from@email.com',
subject='Hello #recipient.first_name#',
message='This is a letter #id#',
context={'id': 453},
language='en',
attachments={'new_test.txt': f},
)
Running this will result in 2 emails queued (because user ben is_blocked and hence is excluded). Subjects will be personalized as “Hello Bob” and “Hello Lena”. Content will be the same: “This is a letter 453”. Both emails have the same attachment.
4.4. Templating¶
sendmail introduces a two-phase approach for creating email templates. This process ensures a flexible and powerful way to handle email templates, leveraging both HTML expertise and user-friendly editing tools.
- HTML Base File Creation
In the first phase, experienced email HTML developers create base files while adhering to the specific limitations of rendering emails in various clients. During this phase, developers can:
Embed images using the {% inline_image %}(see more Inlines) template tag.
Insert placeholders using the {% placeholder %} template tag, which will be filled in the second phase.
These base files act as a foundation for further customization.
- CKEDITOR Placeholders editor
Once the base file is ready, users can move on to the second phase. Using the admin interface, they select the base file and fill in the placeholders defined in the previous phase. In this phase, users can:
Create rich content such as lists, tables, headers, and more features allowed by the configuration in
settings.CKEDITOR_CONFIGS.Embed images, which will automatically be converted to a suitable format for sending via email.
This two-step process provides both technical flexibility for developers and ease of use for non-technical users.
4.4.1. HTML Base File Creation¶
Base Files should be stored in settings.TEMPLATES['DIRS'] / 'email'.
sendmail looks for email folders in all specified DIRS.
In each of your base files you should load sendmail to use custom tags, which can be done as following:
{% load sendmail %}
In your templates you can specify variables to be filled with the context:
{% load sendmail %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example email template</title>
</head>
<body>
Hello, {{ username }}
{% placeholder 'main' %}
</body>
</html>
username variable is expected then to be filled with mail.send() or mail.send_many() context. If it wont be passed user wont see any errors. You can still handle this using django build-in filters, for example:
Hello, {{ username|default:'user'}}
In your templates you may want to use placeholders inside conditions, loops or includes. With sendmail it is possible.
main.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if True %}
{% placeholder 'basic1' %}
{% placeholder 'basic2' %}
{% else %}
{% placeholder 'basic3' %}
{% endif %}
{% include 'email/in.html' %}
</body>
</html>
in.html
{% load sendmail %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% placeholder 'include1' %}
{% placeholder 'include2' %}
</body>
</html>
All placeholders in the previous example will be parsed successfully and provided for users.
Warning
Placeholders are not recognized in child templates when using the Django {% extends %} tag.
4.4.2. Inlines¶
You may want to use embed images to your templates. This can be done using sendmail {% inline_image %} template tag.
<img src="{% inline_image 'images/logo.png' %}" alt="" width="100">
You can specify either alias or absolute path to your image. Alias are resolved:
1. If DEBUG mode is turned on searches in static folder. If not found searches in staticfiles.
If not found raises FileNotFound exception.
If
DEBUGis off searches only instaticfiles. If not found src of your inline image will be empty.
4.4.2.1. Media Images¶
All images inserted in CKEditor fields will be saved in MEDIA_ROOT of your project.
If you for any reason want to insert media images into your html templates:
<img src="{% inline_media_image 'images/logo.png' %}" alt="" width="100">
4.4.3. CKEDITOR Placeholders editor¶
When needed base file was created, users can create 2-phase templates using it. For it you should simply:
Open admin interface and click create new EmailMergeModel.
Enter a name which will be used as an template alias for sending.
Click “Save and continue editing” (This event is also triggered when a template file is changing)
Forms for placeholders editing will appear with defaults, such as:
Placeholder: <name>, Language: <lang_code>
5. Fill these placeholders with your rich content (you can include variables like #var#, #price#, etc. or recipients context (see more EmailAddress and recipient context))
4.5. Multilingual Templates¶
In sendmail you can create and send templates in multiple languages. For this simply edit your settings.py:
Verify that internalization is enabled:
USE_I18N = True
Default templates language can be changed in settings.LANGUAGE_CODE
LANGUAGE_CODE = 'en'
List of all translation languages should be specified in settings.LANGUAGES
LANGUAGES = [
('en', 'English'),
('de', 'German'),
]
Adjust this as needed.
The default language will be used when:
Language for mail.send() is not provided or is not valid (not in
LANGUAGES)if mail.send_many() language is not set and recipient preferred language is
Noneor not valid
If mail.send_many() is called with defined language then all the emails will be forced to that language.
Otherwise each email is translated to recipient preferred language if it is available.
Extra attachments are also translated to this language.
from sendmail.mail import send_many
from sendmail.models import EmailAddress
en_recipient = EmailAddress.objects.create(email='en@gmail.com', first_name='John', preferred_language='en')
de_recipient = EmailAddress.objects.create(email='de@gmail.com', first_name='Ali', preferred_language='de')
send_many(recipients=[en_recipient, de_recipient], emailmerge='your-template', language='en')
In this case de_recipient also gets English copy of an email. To use preferred language you can do something like this:
from sendmail.mail import send_many
from sendmail.models import EmailAddress
en_recipient = EmailAddress.objects.create(email='en@gmail.com', first_name='John', preferred_language='en')
de_recipient = EmailAddress.objects.create(email='de@gmail.com', first_name='Ali', preferred_language='de')
send_many(recipients=[en_recipient, de_recipient], emailmerge='your-template')
Now de_recipient gets German letter and en_recipient English copy.
4.6. Custom Email Backends¶
By default sendmail uses django.core.mail.backends.smtp.EmailBackend.
If you want to use other email backends, you can change it by configuring settings.SENDMAIL['BACKENDS']
For example to use django-ses you can do:
SENDMAIL = {
# other settings
'BACKENDS': {
'default': 'django.core.mail.backends.smtp.EmailBackend',
'ses': 'django_ses.SESBackend',
}
}
Now when you use mail.send() or mail.send_many() you can which backend will be used for sending by specifying
backend argument. If backend is not specified default will be used.
Note For mail.send_many() all generated emails will inherit backend argument.
from sendmail import mail
mail.send(
['recipient@example.com'],
'from@example.com',
subject='Hello',
)
Resulting email will be sent using default backend.
from sendmail import mail
mail.send_many(
recipients=['recipient@example.com', 'next@gmail.com'],
sender='from@example.com',
subject='Hello',
backend='ses'
)
Resulting 2 emails will be sent using django-ses backend.
4.7. Management commands¶
Sendmail commands are available under sendmail namespace and can be triggered as following:
python manage.py sendmail <subcommand> [arguments]
example: python manage.py all -p 4
python manage.py sendmail -h - Show help message.
python manage.py sendmail –version - Show installed version of sendmail.
all - send all queued emails, those are not successfully sent are marked as failed or requeued depending on Settings.
batch - send one batch of queued emails. Batch size is defined in settings(Batch Size).
Argument |
Description |
|---|---|
–processes or -p |
Number of concurrent processes to send queued emails. Defaults to |
–log-level or -l |
Log level |
cleanup_mail - delete all emails created before an X number of days (defaults to 90).
Argument |
Description |
|---|---|
–days or -d |
Email older than this argument will be deleted. Defaults to |
–delete-attachments or -da |
Flag to delete orphaned attachment records and files on disk. If not specified attachments wont be deleted. |
–batch-size or -b |
Limits number of emails being deleted in a batch. Defaults to |
dblocks - when
sendmailis sending emails usingallorbatchmanagement command it blocks the entire database. You can use this command to manage these DB locks.
Argument |
Description |
|---|---|
–delete or -d |
Delete expired locks. |
–delete-all |
Delete all locks. |