Skip to content

Templates

The core renders no template itself. It defines a protocol, and optional packages provide engines:

class TemplateRenderer(Protocol):
    async def render(self, template: str, context: Mapping[str, object]) -> str: ...

Pass a renderer to the Mailer; mailables then name templates in template() and text_template().

Jinja2

pip install "goatnotify[jinja]"
from goatnotify.jinja import JinjaRenderer

renderer = JinjaRenderer.from_directory("templates")
mailer = Mailer(transport, sender="noreply@example.com", renderer=renderer)

from_directory() builds an environment with:

  • HTML autoescaping for .html, .htm and .xml templates (plain-text templates are not escaped);
  • StrictUndefined, so a missing variable is an error instead of an empty string;
  • async rendering.

To use your own environment, pass it directly: JinjaRenderer(environment). Synchronous environments render in a worker thread.

Django templates

from goatnotify.django.templates import DjangoTemplateRenderer

renderer = DjangoTemplateRenderer()  # or DjangoTemplateRenderer(using="jinja2")

It calls render_to_string() through sync_to_async, so it uses your TEMPLATES setting.

A custom renderer

Any object with a matching render coroutine works:

from string import Template


class StringTemplateRenderer:
    def __init__(self, templates: Mapping[str, str]) -> None:
        self._templates = templates

    async def render(self, template: str, context: Mapping[str, object]) -> str:
        return Template(self._templates[template]).substitute(context)