views:

99

answers:

2

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?

+5  A: 

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.

Oli
Oli
Hah, I've been doing this with three different templates which is a PITA. Definite +1 from me!
Van Gale
I like it. I'd always just used separate templates, which works fine, but this is a lot nicer to deal with (especially since you generally want the same context for all the templates anyway).
Carl Meyer
It doesn't support template inheritance at the moment - You need to specify each required block in your actual template. If anybody can figure that out, I'd appreciate it as that would make it much easier to set defaults in a base template... But maybe that's something for settings.py
Oli
A: 

Just use two templates: one for the body and one for the subject.

Till Backhaus
Two files means two filenames for essentially the same thing. It's just a pain in the behind having to keep on top of that many templates.
Oli