Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Email templating in django

As we all know (or should), you can use Django's template system to render email bodies:

def email(email, subject, template, context):
    from django.core.mail import send_mail
    from django.template import loader, Context

    send_mail(subject, loader.get_template(template).render(Context(context)), '[email protected]', [email,])

This has one flaw in my mind: to edit the subject and content of an email, you have to edit both the view and the template. While I can justify giving admin users access to the templates, I'm not giving them access to the raw python!

What would be really cool is if you could specify blocks in the email and pull them out separately when you send the email:

{% block subject %}This is my subject{% endblock %}
{% block plaintext %}My body{% endblock%}
{% block html %}My HTML body{% endblock%}

But how would you do that? How would you go about rendering just one block at a time?

like image 552
Oli Avatar asked Jan 12 '10 18:01

Oli


1 Answers

This is my third working iteration. It assuming you have an email template like so:

{% block subject %}{% endblock %}
{% block plain %}{% endblock %}
{% block html %}{% endblock %}

I've refactored to iterate the email sending over a list by default and there are utility methods for sending to a single email and django.contrib.auth Users (single and multiple). I'm covering perhaps more than I'll sensibly need but there you go.

I also might have gone over the top with Python-love.

def email_list(to_list, template_path, context_dict):
    from django.core.mail import send_mail
    from django.template import loader, Context

    nodes = dict((n.name, n) for n in loader.get_template(template_path).nodelist if n.__class__.__name__ == 'BlockNode')
    con = Context(context_dict)
    r = lambda n: nodes[n].render(con)

    for address in to_list:
        send_mail(r('subject'), r('plain'), '[email protected]', [address,])

def email(to, template_path, context_dict):
    return email_list([to,], template_path, context_dict)

def email_user(user, template_path, context_dict):
    return email_list([user.email,], template_path, context_dict)

def email_users(user_list, template_path, context_dict):
    return email_list([user.email for user in user_list], template_path, context_dict)

As ever, if you can improve on that, please do.

like image 124
Oli Avatar answered Nov 11 '22 00:11

Oli