Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A Django pattern for creating customizable, re-useable email templates. Admins can define a number of standard templates, and these templates are then injected into email forms as default content, which can then be selected from and customized on the fly before being sent.
# Models.py
class EmailTemplate(models.Model):
''' Templates for standard emails. '''
body = models.TextField(verbose_name="The body of the email")
subject = models.CharField(max_length=200, verbose_name="Default Subject Line")
name = models.CharField(max_length=200, verbose_name="Template Name")
creator = models.ForeignKey(User)
# Forms.py
class EmailTemplateForm(forms.Form):
''' We don't actually make this a model form even though it's a derivative
function of a model because it's not directly constructed from the model fields
itself.
Separating out a base form means multiple derivative types of EmailTemplateForms
can be created for different contexts.
'''
sender = forms.EmailField(widget=forms.TextInput(attrs={'readonly':'readonly', 'class':"form-control"}))
recipient = forms.EmailField(widget=forms.TextInput(attrs={'class':"form-control"}))
footer = forms.CharField( widget=forms.Textarea(attrs={'readonly':'readonly', 'class':"form-control"}))
subject = forms.CharField(widget=forms.TextInput(attrs={'class':"form-control"}))
body = forms.CharField(widget=forms.Textarea(attrs={'class':"form-control"}))
class EventEmailTemplateForm(EmailTemplateForm):
def __init__(self, tpl, subscription, location):
''' pass in an EmailTemplate instance, and an whatever else you need to construct the default content.
In this case, I'm using a subscription object. '''
domain = Site.objects.get_current().domain
# calling super will initialize the form fields
super(SubscriptionEmailTemplateForm, self).__init__()
# add in the default content for each of the fields. these will all be editable
# by the user (except the readonly field) because it is just default content in
# a form field.
self.fields['sender'].initial = location.from_email()
# in our case we always cc the admin email, of course this is optional.
self.fields['recipient'].initial = "%s, %s" % (subscription.user.email, location.from_email())
# make the footer field readonly because we don't want the users editing that part.
self.fields['footer'].initial = forms.CharField(
widget=forms.Textarea(attrs={'readonly':'readonly'})
)
self.fields['footer'].initial = '''--------------------------------\nYour membership id is %d. Manage your membership from your profile page https://%s/people/%s.''' % (subscription.id, domain, subscription.user.username)
template_variables = {
'subscription': subscription,
}
self.fields['subject'].initial = '['+location.email_subject_prefix+'] ' + Template(tpl.subject).render(Context(template_variables)) + ' (#' + str(subscription.id) + ')'
self.fields['body'].initial = Template(tpl.body).render(Context(template_variables))
# Views.py
def SomeView(request, ...):
# do stuff...
default_email = SubscriptionEmailTemplateForm(email_template, subscription, location)
# do other stuff, then pass the form to the context for rendering
return render(request, 'subscription_manage.html', {'default_email': default_email, ... })
# Some_Template.html
<form id="action-email-user" method=POST action="{% url 'subscription_send_mail' location.slug s.id %}">
{% csrf_token %}
<div class="form-group">
<span class="email-form-label">To: </span> {{f.recipient}} <br>
<span class="email-form-label">From: </span> {{f.sender}} <br>
<span class="email-form-label">Subject: </span> {{f.subject}} <br>
{{f.body}}
{{f.footer}}
</div>
<submit value="Send">
<button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
<button type="submit" class="btn btn-primary">Send Email</button>
</form>
# use whatever method you prefer to process the actual sending of email on the submit view. We use Mailgun.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.