django-anymail
Django Anymail provides a unified interface for sending transactional emails and receiving webhooks across many Email Service Providers (ESPs) like SendGrid, Mailgun, Postmark, and more. It integrates seamlessly with Django's `EmailBackend` and `send_mail` functions, adding advanced features like ESP-specific options, transactional tracking, and inbound email signals. The library is actively maintained with frequent releases, currently at version 14.0, and supports various Django and Python versions.
Warnings
- breaking Version 14.0 introduced improved Unicode handling that might surface new `AnymailUnicodeError` exceptions for previously silently-handled problematic non-ASCII characters. This helps identify and fix issues at the source.
- breaking Version 11.0 introduced breaking changes for Amazon SES and SparkPost. For Amazon SES, `anymail.message.AnymailMessage.esp_extra` now strictly requires a dictionary. For SparkPost, using `template_id` requires explicitly setting `use_org_defaults=True`.
- gotcha Anymail webhook views, designed to receive POST requests from external ESPs, are incompatible with Django's default CSRF protection. If not properly exempted, these webhook requests will be blocked.
- gotcha The most common issue is incorrectly configuring the `ANYMAIL` dictionary in `settings.py` or omitting the specific ESP API key (e.g., `MAILGUN_API_KEY`, `SENDGRID_API_KEY`, `POSTMARK_API_TOKEN`). This will prevent Anymail from authenticating with your chosen ESP.
Install
-
pip install django-anymail -
pip install django-anymail[mailgun]
Imports
- AnymailWebhooksView
from anymail.webhooks.views import AnymailWebhooksView
- AnymailMessage
from anymail.message import AnymailMessage
Quickstart
import os
from django.conf import settings
from django.core.mail import send_mail
from anymail.message import AnymailMessage
# --- Minimal Django settings configuration for standalone execution ---
# In a real Django project, these settings would be in your settings.py file.
# This block ensures the script can run without a full Django project setup.
if not settings.configured:
settings.configure(
ANYMAIL={
"MAILGUN_API_KEY": os.environ.get("ANYMAIL_MAILGUN_API_KEY", "your-mailgun-api-key"),
"MAILGUN_SENDER_DOMAIN": os.environ.get("ANYMAIL_MAILGUN_SENDER_DOMAIN", "mg.example.com"),
# For other ESPs, replace MAILGUN_API_KEY/SENDER_DOMAIN with appropriate keys/settings
},
EMAIL_BACKEND="anymail.backends.mailgun.EmailBackend", # Or your chosen ESP backend
DEFAULT_FROM_EMAIL="sender@example.com",
DEBUG=True, # Minimal required setting for settings.configure
)
print("Attempting to send an email using Django's send_mail...")
try:
# Example 1: Basic email using Django's send_mail
send_mail(
subject="Hello from django-anymail!",
message="This is a test email sent via Mailgun through django-anymail.",
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=["recipient@example.com"], # Replace with a valid recipient email
fail_silently=False,
)
print("Basic email send attempt successful.")
# Example 2: Using AnymailMessage for ESP-specific features (e.g., tags)
message = AnymailMessage(
subject="AnymailMessage with Tags",
body="This email demonstrates using AnymailMessage with custom tags.",
from_email=settings.DEFAULT_FROM_EMAIL,
to=["recipient2@example.com"], # Replace with another valid recipient email
)
message.tags = ["transactional", "test-tag"]
# You can also add merge_data, metadata, etc.
message.send(fail_silently=False)
print("AnymailMessage with tags send attempt successful.")
except Exception as e:
print(f"\nFailed to send email: {e}")
print("Please ensure your environment variables (e.g., ANYMAIL_MAILGUN_API_KEY, ANYMAIL_MAILGUN_SENDER_DOMAIN) ")
print("are correctly set and your ESP configuration in settings.py is valid.")
print("Note: In a real Django project, you'd only need the settings in settings.py ")
print("and directly call send_mail or AnymailMessage.send().")