Skip to content

Instantly share code, notes, and snippets.

@darthwade
Created July 19, 2020 01:28
Show Gist options
  • Save darthwade/097ddadf47d50d683846fbec5400e5ad to your computer and use it in GitHub Desktop.
Save darthwade/097ddadf47d50d683846fbec5400e5ad to your computer and use it in GitHub Desktop.
Django email templates where subject and text/html body are in the same file
from django.conf import settings
from django.core import mail
from django.template.context import make_context
from django.template.loader import get_template
from django.template.loader_tags import BlockNode, ExtendsNode
class BaseEmailMessage(mail.EmailMultiAlternatives):
template_name = None
context = {}
html = None
_node_map = {
"subject": "subject",
"text": "body",
"html": "html",
# TODO: support extends "email/base.html"
# "text_body": "body",
# "html_body": "html",
}
def __init__(self, template_name=None, context=None, *args, **kwargs):
super(BaseEmailMessage, self).__init__(*args, **kwargs)
if template_name is not None:
self.template_name = template_name
if context is not None:
self.context = context
def get_template_name(self):
return self.template_name
def get_context_data(self):
return self.context
def send(self, *args, **kwargs):
self.render()
super(BaseEmailMessage, self).send(*args, **kwargs)
def render(self):
template = get_template(self.get_template_name())
context = make_context(self.get_context_data())
with context.bind_template(template.template):
blocks = self._get_blocks(template.template.nodelist, context)
for block_node in blocks.values():
self._process_block(block_node, context)
self._attach_body()
def _process_block(self, block_node, context):
attr = self._node_map.get(block_node.name)
if attr is not None:
setattr(self, attr, block_node.render(context).strip())
def _get_blocks(self, nodelist, context):
blocks = {}
for node in nodelist:
if isinstance(node, ExtendsNode):
parent = node.get_parent(context)
blocks.update(self._get_blocks(parent.nodelist, context))
blocks.update(
{node.name: node for node in nodelist.get_nodes_by_type(BlockNode)}
)
return blocks
def _attach_body(self):
if self.body and self.html: # when both text_body and html_body provided
self.attach_alternative(self.html, "text/html")
elif self.html: # when only html_body provided
self.body = self.html
self.content_subtype = "html"
def send_email(
template_name,
to,
context=None,
attachments=None,
cc=settings.EMAIL_DEFAULT_CC,
bcc=settings.EMAIL_DEFAULT_BCC,
reply_to=settings.EMAIL_DEFAULT_REPLY_TO,
from_email=settings.DEFAULT_FROM_EMAIL,
headers=settings.EMAIL_DEFAULT_HEADERS,
fail_silently=settings.EMAIL_DEFAULT_FAIL_SILENTLY,
):
return BaseEmailMessage(
template_name=template_name,
to=to,
context={**settings.EMAIL_DEFAULT_TEMPLATE_CONTEXT, **context},
attachments=attachments,
cc=cc,
bcc=bcc,
reply_to=reply_to,
from_email=from_email,
headers=headers,
).send(fail_silently=fail_silently)
{% block subject %}[Action Required] ⚠️ {{user.first_name}}, review your information{% endblock %}
{% block text %}
Hi {{user.first_name}},
{% endblock %}
{% block html %}
<p>Hi {{user.first_name}},</p>
{% endblock %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment