Skip to content

feat: add Django email backend, supporting Django 4.2 LTS through 6.1 - #26

Merged
gregsvo merged 1 commit into
mainfrom
feature/django-backend
Aug 12, 2026
Merged

feat: add Django email backend, supporting Django 4.2 LTS through 6.1#26
gregsvo merged 1 commit into
mainfrom
feature/django-backend

Conversation

@gregsvo

@gregsvo gregsvo commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds postmark.django.EmailBackend, a drop-in EMAIL_BACKEND, gated behind a new optional django extra (pip install postmark-python[django]). Supports Django 4.2 LTS, 5.2 LTS, 6.0, and 6.1.
  • The Postmark payload is built from EmailMessage's high-level attributes (to/cc/bcc/subject/body/alternatives/attachments/extra_headers) rather than EmailMessage.message(), so it isn't affected by Django 6.0's modernized email API (message() now returns a different underlying type).
  • Adds PostmarkEmailMessage / PostmarkEmailMultiAlternatives / PostmarkEmailMixin for tag, metadata, message_stream, and pre_send / post_send / on_exception signals.
  • New settings: POSTMARK_SERVER_TOKEN, POSTMARK_TEST_MODE, POSTMARK_TRACK_OPENS, POSTMARK_MESSAGE_STREAM.
  • Also included: PyPI keywords, CONTRIBUTING.md, and GitHub issue templates for discoverability/contribution ergonomics.

Backward compatibility: zero changes to any pre-existing file under postmark/ (verified via git diff), and the core dependencies list in pyproject.toml is untouched — django only appears in the new optional extra and the dev group, so existing users get no new transitive dependency. Verified pytest still passes in full with Django uninstalled (422 tests), and the whole suite (454 tests) passes with it installed against both Django 4.2 and 5.2 locally.

Test plan

  • poetry run pytest — 454 passed, 96% coverage
  • poetry run ruff check . / ruff format --check . — clean
  • poetry run mypy postmark/ — clean
  • tests/django_backend/ (32 tests) run manually against Django 4.2 and 5.2 locally; CI's new django-tests job matrixes 4.2/5.2/6.0/6.1 on Python 3.12 (the one Python version all four Django series support)
  • Confirmed import postmark never imports django, and bare pytest no longer crashes collection when the django extra isn't installed
  • CI green on this PR (test, django-tests, lint)

Add postmark.django.EmailBackend, a drop-in EMAIL_BACKEND gated behind a
new optional `django` extra (pip install postmark-python[django]):

- EmailBackend builds the Postmark payload from EmailMessage's high-level
  attributes (to/cc/bcc/subject/body/alternatives/attachments/extra_headers)
  rather than EmailMessage.message(), so it isn't affected by Django 6.0's
  modernized email API.
- PostmarkEmailMessage / PostmarkEmailMultiAlternatives / PostmarkEmailMixin
  for setting tag, metadata, and message_stream.
- pre_send / post_send / on_exception signals.
- Settings: POSTMARK_SERVER_TOKEN, POSTMARK_TEST_MODE, POSTMARK_TRACK_OPENS,
  POSTMARK_MESSAGE_STREAM.

Testing and CI:
- tests/django_backend/ (32 tests: sending, batching/chunking, attachments,
  headers, Unicode round-trip, fail_silently, signals, a regression test
  proving .message() is never called).
- New django-tests CI job matrixed across Django 4.2/5.2/6.0/6.1.
- tests/conftest.py now skips collecting tests/django_backend/ gracefully
  when the django extra isn't installed, instead of failing collection for
  the whole suite.

Docs and examples:
- examples/django/ (simple send, batch, attachment, custom header, external
  image, tags/metadata, settings snippet).
- README Django section with install/quickstart.

Also included: PyPI keywords, CONTRIBUTING.md, and GitHub issue templates
for discoverability and contribution ergonomics.
Comment thread tests/conftest.py
import pytest

try:
import django # noqa: F401
from dotenv import load_dotenv

load_dotenv()
except ImportError:
from dotenv import load_dotenv

load_dotenv()
except ImportError:
from dotenv import load_dotenv

load_dotenv()
except ImportError:
from dotenv import load_dotenv

load_dotenv()
except ImportError:
from dotenv import load_dotenv

load_dotenv()
except ImportError:
from dotenv import load_dotenv

load_dotenv()
except ImportError:
Comment thread tests/conftest.py
except ImportError:
# The `django` extra is optional; don't let its absence break collection
# of the rest of the suite.
collect_ignore = ["django_backend"]
@gregsvo
gregsvo merged commit 61109a3 into main Aug 12, 2026
14 checks passed
@gregsvo
gregsvo deleted the feature/django-backend branch August 12, 2026 21:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant