Metadata-Version: 2.5
Name: django-mjml-templates
Version: 1.0.0
Summary: A drop-in Django template engine that also renders .mjml email templates
Project-URL: Homepage, https://github.com/SmileyChris/django-mjml-templates
Project-URL: Changelog, https://github.com/SmileyChris/django-mjml-templates/blob/main/CHANGELOG.md
Author-email: Chris Beaven <chris@tactful.co.nz>
License-Expression: MIT
License-File: LICENSE
Keywords: django,email,mjml,templates
Classifier: Development Status :: 5 - Production/Stable
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.0
Classifier: Framework :: Django :: 5.1
Classifier: Framework :: Django :: 5.2
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: Email
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.12
Requires-Dist: django>=4.2
Requires-Dist: html2text>=2024.2
Requires-Dist: mjml-python>=1.3
Description-Content-Type: text/markdown

# django-mjml-templates

A drop-in replacement for Django's template engine that also understands `.mjml` files.

```sh
uv add django-mjml-templates   # or pip install
```

Swap the backend; everything else about `TEMPLATES` stays as it was. The engine is still
named `django` (Django takes the default name from the module segment of `BACKEND`), so
`engines["django"]` and `using="django"` keep working.

```python
TEMPLATES = [{
    "BACKEND": "mjml_templates.django.DjangoTemplates",   # was django.template.backends.django.DjangoTemplates
    "APP_DIRS": True,
    "OPTIONS": {"context_processors": [...]},
}]
```

Every other template renders exactly as before. A template whose name ends in `.mjml`
(or a `from_string` whose code starts with `<mjml`) is a Django template first —
`{% extends %}`, loops, `{% url %}`, autoescaping — and the MJML that produces is
compiled to email HTML on render.

```html
{# templates/email/welcome.mjml #}
<mjml>
  <mj-head><mj-title>Welcome, {{ user.first_name }}</mj-title></mj-head>
  <mj-body>
    <mj-section><mj-column>
      <mj-text>Hi {{ user.first_name }}, thanks for signing up.</mj-text>
      <mj-button href="{{ link }}">Get started</mj-button>
    </mj-column></mj-section>
  </mj-body>
</mjml>
```

```python
from django.template.loader import get_template

get_template("email/welcome.mjml").render_email({"user": user, "link": link}, to=[user.email]).send()
```

`render_email(context, request=None, *, subject="", **kwargs)` returns an unsent
`EmailMultiAlternatives` with the plain-text body and the HTML alternative attached, so
you can still `attach()` a file before sending. `kwargs` go to `EmailMultiAlternatives`:
`to`, `from_email`, `bcc`, `reply_to`, `headers`, and so on.

For the raw pieces, `render_email_parts(context, request=None, *, subject="")` returns
a `(subject, text, html)` named tuple. Both are on `.mjml` templates only:

- **subject** — as given, else the rendered `<mj-title>`, else `ValueError`.
- **text** — `email/welcome.txt` rendered with the same context if it exists, else
  the HTML run through `html2text`.
- **html** — the compiled email.

MJML is compiled with [mjml-python](https://pypi.org/project/mjml-python/) (the Rust `mrml`
port), so there is no Node dependency. Plain-text fallback uses `html2text`.
