Transactional Emails
Postmark is a Paid Service
With 100 emails / month as their free tier, however, this is suitable for testing purposes. Afterwards, when the app starts receiving traction, pricing starts at $15/month for up to 10k emails sent/received.
Email Backend
How do I send emails to the user when the following transactions happen?
- verify email address on signup; or
- reset password in case it's been forgotten?
In development mode, I use Django's email server at django.core.mail
for handling transactional emails so that the email is served on the terminal rather than an smtp server: This is the import of the following line in config.settings
:
- The default is
"django.core.mail.backends.smtp.EmailBackend"
. This is considered the console backend. It will also be overriden, depending onPOSTMARK_API_KEY
insettings/__init__.py
"""
To guard against unset email servers in non-dev environments, I create the following some non-default env variables and check if they've been set by the user.
# in /config/settings/_auth.py
_email = "do@configure.separately" # (1)
EMAIL_RECIPIENT = env("EMAIL_RECIPIENT", _email) # (2)
EMAIL_SENDER = env("EMAIL_SENDER", _email) # (3)
EMAIL_NOT_CONFIGURED = None # If populated by criteria, use as error message.
if not all([_email != EMAIL_RECIPIENT, _email != EMAIL_SENDER]):
EMAIL_NOT_CONFIGURED = (f"{EMAIL_SENDER=} and {EMAIL_RECIPIENT=} must be configured.")
# in /config/settings/__init__.py
if ENV_NAME in ["prod", "test"]:
...
if POSTMARK_API_KEY: #
EMAIL_BACKEND = "postmark.django_backend.EmailBackend" # (4)
if EMAIL_NOT_CONFIGURED:
raise EnvError(EMAIL_NOT_CONFIGURED)
- Placeholder for next 2 variables to ensure that this gets replaced, see related
EMAIL_NOT_CONFIGURED
. - Needed by contact form in pages/tasks.py, see
background_send_contact_form_email()
- Transactional emails require a separate email server setup.
- Notice how this overrides the original setting in
dev
In contrast, when ENV_NAME
is test
/prod
, a proper email server like Sendgrid, Postmark, Mailgun, Amazon SES etc. will need to be setup. For now, I'll use Postmark.
Postmark Setup
Ensure setup in the postmark web app before proceeding. This requires a Postmark account and a previously acquired domain name, e.g. mv3.dev
is a domain name. General steps to follow:
- Postmark requires to add the domain.
-
DKIM
andReturn-Path
DNS records will be revealed afterwards. It will look something like this:Hostname Type Value DKIM xxxxxx._domainkey TXT Return-Path pm-bounces CNAME -
These records will need to be inputted in the domain registrar, e.g. Google, Cloudflare, Namecheap, etc.
- I'll need to set these records from Postmark... in Namecheap since this is where I registered the domain.
- After setup in Namecheap, I revisit Postmark and in each of the records, click verify.
- After everything passes, generate
POSTMARK_API_KEY
from the dashboard, e.g.xxx
, and add it to the.env
file:
POSTMARK_API_KEY=i-am-the-secret
EMAIL_RECIPIENT=i-can-be-any-forwardable-email-address
EMAIL_SENDER=i-need-to-configured-as-a-sender-in-postmark
# This additional configuration is already set in /_auth.py, see https://github.com/themartorana/python-postmark#django
POSTMARK_SENDER = EMAIL_SENDER # (1)
POSTMARK_TEST_MODE = False
POSTMARK_TRACK_OPENS = True
POSTMARK_RETURN_MESSAGE_ID = True
DEFAULT_FROM_EMAIL = EMAIL_SENDER # (2)
- Seeming duplication is intentional. In the event, I use Postmark, I just need to set
POSTMARK_SENDER
. If I need to switch to a different provider, I still retain the same logic forEMAIL_SENDER
. - Overrides default
webmaster@localhost
; without it, when running Postmark, results inUnprocessable Entity: Invalid 'From' address: 'webmaster@localhost'.
POSTMARK_API_KEY=op://dev/postmark/credential
EMAIL_RECIPIENT=op://dev/start-django/email/recipient
EMAIL_SENDER=op://dev/start-django/email/sender
See same discussion on secret references in Social Auth setup.
Test Configuration
- Add .env vars:
ENV_NAME=test
andPOSTMARK_API_KEY=xxx
- Run
docker-compose --env-file .env up --build
- Login via regular email address and password
- Check that email has been received
-
Note that without formatting, the email will look barren:
6. Email markup in 2023 is still a headache. Consider notes from Cerberus templates, Maizzle, etc.