Skip to main content

django-comms

Reusable email communications, conversations, subscriptions, and delivery tracking for Django.

The package currently supports Mailgun, Django-configured email backends, SMTP, inbound email, delivery events, attachments, scheduled delivery through Celery, throttled sending, and PostgreSQL-backed subscription history.

Requirements

  • Python 3.11 or newer
  • Django 5.2 or 6.0
  • PostgreSQL
  • Celery
  • libmagic

Installation

uv add django-comms

Add the application:

INSTALLED_APPS = [
    # Django applications...
    "django_comms",
]

Include the Mailgun webhook URLs:

from django.urls import include, path

urlpatterns = [
    path("comms/", include("django_comms.urls")),
]

Apply the migrations:

python manage.py migrate

Mailboxes and backends

A mailbox scopes addresses, conversations, topics, and delivery backends. Create one default mailbox when callers should be able to omit mailbox=:

from django_comms.models import Mailbox

mailbox = Mailbox.objects.create(
    name="Main mailbox",
    identifier="main",
    default_from_name="Example Organization",
    default_from_email="hello@example.com",
    is_default=True,
)

Configure Mailgun through MessagingBackend.config:

from django_comms.constants import MessagingBackendClass
from django_comms.models import MessagingBackend

MessagingBackend.objects.create(
    mailbox=mailbox,
    label="Mailgun",
    identifier="mailgun",
    backend_class_path=MessagingBackendClass.MAILGUN,
    is_default=True,
    config={
        "base_url": "https://api.eu.mailgun.net/v3/example.com",
        "validation_url": "https://api.mailgun.net/v4/address/validate",
        "api_key": "...",
        "signing_key": "...",
    },
)

The webhook endpoints are then:

/comms/mailgun/<backend-id>/events/
/comms/mailgun/<backend-id>/inbound/

A Django backend uses the configured EMAIL_BACKEND and email settings:

MessagingBackend.objects.create(
    mailbox=mailbox,
    label="Django email",
    identifier="django-email",
    backend_class_path=MessagingBackendClass.DJANGO,
)

SMTP is also supported for outbound delivery. Configure it with the SMTP server connection details:

MessagingBackend.objects.create(
    mailbox=mailbox,
    label="SMTP",
    identifier="smtp",
    backend_class_path=MessagingBackendClass.SMTP,
    is_default=True,
    config={
        "host": "smtp.example.com",
        "port": 587,
        "username": "smtp-user",
        "password": "...",
        "use_tls": True,
        "use_ssl": False,
        "timeout": 30,
    },
)

SMTP submission provides synchronous acceptance only. It does not provide Mailgun delivery, open, click, bounce, complaint, or inbound-mail events.

Sending can be throttled per backend. A null throttle_limit disables throttling; deferred messages are picked up by the scheduled dispatcher:

from datetime import timedelta

MessagingBackend.objects.create(
    mailbox=mailbox,
    label="Throttled SMTP",
    identifier="throttled-smtp",
    backend_class_path=MessagingBackendClass.SMTP,
    throttle_limit=10,
    throttle_period=timedelta(minutes=1),
    config={"host": "smtp.example.com"},
)

Preparing email

Given emails/invoice.html and emails/invoice.txt:

from django_comms import prepare_email

prepared = prepare_email(
    "invoice:123",
    mailbox=mailbox,  # Optional when a default mailbox exists.
    recipients=["Alice <alice@example.com>"],
    subject="Your invoice",
    template="emails/invoice",
    context={"invoice": invoice},
)

Choose one persistence operation:

message = prepared.persist()  # Store without dispatching.
message = prepared.send()  # Store and dispatch through Celery.
message = prepared.schedule(timestamp)  # Store for scheduled dispatch.

A manually dispatched message can bypass backend throttling:

from django_comms import tasks

tasks.dispatch_message.delay(message.pk, ignore_throttling=True)

Configure Celery beat to dispatch due messages:

CELERY_BEAT_SCHEDULE = {
    "django-comms-dispatch": {
        "task": "django_comms.tasks.dispatch_scheduled_messages",
        "schedule": 60,
    },
}

Plaintext templates render with autoescape=False. A custom Django template engine alias can be supplied for either format:

template = {
    "html": ("emails/invoice.html", "email_html"),
    "plaintext": ("emails/invoice.txt", "email_plaintext"),
}

Contact model

Email addresses and subscriptions refer to the configured contact model. It defaults to AUTH_USER_MODEL.

Set a different model before the first migration:

DJANGO_COMMS_CONTACT_MODEL = "contacts.Contact"
DJANGO_COMMS_CONTACT_ADAPTER = "contacts.adapters.ContactAdapter"

Adapters derive values used by the admin and subscription exports:

from django_comms.adapters import ContactAdapter


class ContactAdapter(ContactAdapter):
    search_fields = ("display_name", "primary_email")

    def get_email(self, contact):
        return contact.primary_email

    def get_first_name(self, contact):
        return contact.given_name

    def get_last_name(self, contact):
        return contact.family_name

Changing the contact model after applying the initial migration is not supported.

Storage

Attachments use the default Django storage unless an alias is configured:

DJANGO_COMMS_STORAGE_ALIAS = "private"

STORAGES = {
    "default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
    "private": {
        "BACKEND": "storages.backends.s3.S3Storage",
        "OPTIONS": {
            "bucket_name": "private-files",
            "default_acl": "private",
        },
    },
}

The storage setting reference is preserved in migrations.

Other settings

# Suppress outbound delivery while marking messages as dispatched.
# Defaults to DEBUG when omitted. Mailgun uses its test mode; SMTP does not
# connect to the server.
COMMS_TEST_MODE = True

# Namespace used by package-local admin URL helpers.
DJANGO_COMMS_ADMIN_SITE_NAME = "admin"

For a custom AdminSite, call django_comms.admin.register_admin(site).

Development

uv sync
uv run ruff format .
uv run ruff check .
uv run pytest

Tests require PostgreSQL. To test a custom contact model separately:

DJANGO_SETTINGS_MODULE=tests.custom_settings \
    uv run pytest custom_contact_tests

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_comms-0.1.0.tar.gz (105.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_comms-0.1.0-py3-none-any.whl (49.6 kB view details)

Uploaded Python 3

File details

Details for the file django_comms-0.1.0.tar.gz.

File metadata

  • Download URL: django_comms-0.1.0.tar.gz
  • Upload date:
  • Size: 105.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for django_comms-0.1.0.tar.gz
Algorithm Hash digest
SHA256 dcaeeb919d3e79cce6ab243949224af0ffb81beed9c48084d7ff5c648015efe2
MD5 ac278980a03ca5bc20e3b953cbc0450a
BLAKE2b-256 64a7ff4b6963d779d9e695c7cbf3203992e67c9e2a33fd798f9a5c336b7379a7

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_comms-0.1.0.tar.gz:

Publisher: ci.yml on GaretJax/django-comms

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_comms-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: django_comms-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 49.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for django_comms-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5740140d1fc887595dbf87b54735a27643ca556f3960ccad2db9e33672013f28
MD5 f84e06e1bd1d07e2f3a594810c1a621e
BLAKE2b-256 d11a9183d26997a83d5cc1d2ba8a332a80885e1ef2ac8cbc527678af3e93bbeb

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_comms-0.1.0-py3-none-any.whl:

Publisher: ci.yml on GaretJax/django-comms

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.0

2 files

This release

0.1.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page