Opened 13 years ago
Last modified 4 months ago
#17193 new New feature
Send templated email.
Reported by: | Tom Christie | Owned by: | julianapplebaum |
---|---|---|---|
Component: | Core (Mail) | Version: | |
Severity: | Normal | Keywords: | |
Cc: | Chris Streeter, artem.rizhov@…, chuck-norris@…, cmawebsite@… | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | yes |
Needs tests: | yes | Patch needs improvement: | yes |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
If your sending email it's likely that you want to render the body text from template, but there's currently no shortcut to send an email based on a template.
The attached patch is based on a stripped down version of https://github.com/bradwhittington/django-templated-email
It adds django.shortcuts.send_templated_mail
, which mirrors the existing send_mail
, but which renders the subject and body of the mail from a template, rather than taking their values explicitly. It also supports multipart html/plaintext emails.
The docs will look something like this...
**send_templated_mail(template_name, from_email, recipient_list, dictionary=None, context_instance=None, fail_silently=False, auth_user=None, auth_password=None, connection=None):** Sends a mail, rendering the subject and body of the email from a template. The template should contain a block named 'subject', and either/both of a 'plain' and/or 'html' block. If only the 'plain' block exists, a plaintext email will be sent. If only the 'html' block exists, the plaintext component will be automatically generated from the html, and a multipart email will be sent. If both the 'plain' and 'html' blocks exist, a multipart email will be sent. **Required arguments:** `template_name` - The template that should be used to render the email. `from_email` - The sender's email address. `recipient_list` - A list of reciepient's email addresses. **Optional arguments:** `dictionary` - The context dictionary used to render the template. By default, this is an empty dictionary. `context_instance` - The Context instance used to render the template. By default, the template will be rendered with a Context instance (filled with values from dictionary). `fail_silently` - As in `send_mail`. `auth_user` - As in `send_mail`. `auth_password` - As in `send_mail`. `connection` - As in `send_mail`.
Attachments (5)
Change History (40)
by , 13 years ago
Attachment: | send_templated_mail.diff added |
---|
comment:1 by , 13 years ago
Needs documentation: | set |
---|---|
Needs tests: | set |
comment:2 by , 13 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:3 by , 13 years ago
I'm new to to the django dev community, and was hoping to write some unit tests for this shortcut. Should I claim the ticket before doing that? Sorry if this isn't the right place to be asking that.
Thanks,
Julian Applebaum
follow-up: 7 comment:4 by , 13 years ago
Yes, you should assign the ticket to yourself if you intend to work on it.
You'll find more information about our development process in the contributing guide.
by , 13 years ago
Attachment: | send_templated_mail.2.diff added |
---|
comment:5 by , 13 years ago
Needs documentation: | unset |
---|
Updated the patch to include documentation.
Also tweaked the API slightly - the email connection and message sending has been decoupled from the message generation, and I've made some tweaks to the arguments that send_templated_mail
takes.
I think this should pretty much hit the sweet spot of making sure the shortcut interface isn't overly complicated, whilst still providing for the common case.
by , 13 years ago
Attachment: | send_templated_mail.3.diff added |
---|
comment:6 by , 13 years ago
Added some extra documentation in the existing email section, referring to the shortcut.
comment:7 by , 13 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
Replying to aaugustin:
Yes, you should assign the ticket to yourself if you intend to work on it.
You'll find more information about our development process in the contributing guide.
Thanks, and will do. Should have the patch up by Sunday night.
comment:8 by , 13 years ago
Quick status update:
It took me and the group I'm with longer than anticipated to get everything up and running. We've written a few unit tests for _render_mail, and aim to finish the rest this coming week. We apologize for the delay.
Julian
comment:9 by , 13 years ago
We apologize for the delay.
Nothing to apologize for, it's a community effort - everything's appreciated. :)
comment:10 by , 13 years ago
I have a few doubts about how this has been designed. With both html and plain 'templates' actually just being blocks of a single template, it is not possible to use template inheritance to design consistent emails, which is a common use of the template system. Without this, styles would need to be replicated across multiple templates.
In our send_mail wrapper at $JOB, we allow the user to pass in the stub name of a template. The utility then attempts to render the templates stub+'.html' and stub+'.txt', and uses the results of these to determine whether we send a text/plain, text/html or multipart/alternative.
Instead of extracting the subject from a template, we allow it to be passed in directly. We also add a language argument, which is used to activate the appropriate translation prior to rendering the emails, or coercing the subject to unicode. This allows us to send fully translated emails appropriate for the user receiving the email - which may not be the same as the language currently activated, if there is any, or the language that has been activated for the current view.
Cheers
Tom (one of these days I'll remember my trac login)
comment:11 by , 13 years ago
It's not quite true that you can't use template inheritance with this style, although I've come around to thinking that it probably is a an abuse of the 'block' tags to change their semantics for email templates.
I don't like the stub+'.txt', stub+'.html' for 2 reasons:
Firstly it's very easy for the two separate files to get out of sync, with no easy way of noticing that's happened.
Secondly the stub + extension style isn't used anywhere else.
I think a better solution would be a single template and a flag to switch between plaintext only, or html + autogenerated plaintext.
It's not impossible that I might get a spare couple of hours between now and the 1.4 checkin deadline, but it's not looking all that likely, so may have to defer this to 1.5.
by , 13 years ago
Attachment: | send_templated_mail_4.diff added |
---|
comment:12 by , 13 years ago
Hey Everyone
Just uploaded the diff file for my group's unit tests. Let me know if you run into any issues with the tests/the diff file, etc.
Best,
Julian
comment:13 by , 13 years ago
Thanks Julian,
There may be some redesign needed of this feature, but either way those tests look fairly comprehensive - should come in useful.
Tom
comment:14 by , 13 years ago
Needs tests: | unset |
---|
follow-up: 18 comment:15 by , 13 years ago
Triage Stage: | Accepted → Design decision needed |
---|
Thank you all for your great work on this patch. However, I'm struggling a bit to see the value of adding this much new code for something that is already quite easily achievable using render_to_string()
and separate templates.
I do agree that creating html+plain emails is still a little too hard in Django, but in my opinion this could be simplified by introducing a new helper class wrapping around EmailMultiAlternatives
and providing a simpler API and sensible defaults. For example, something like: HTMLEmailMessage(body_plain='...', body_html='...')
In any case, I personally think that any template rendering should be left to the user to do separately (for example by using render_to_string()
).
For now I'm marking this ticket as DDN instead of wontfixing so that more discussion can occur.
comment:16 by , 13 years ago
Agreed that this isn't quite the right way to approach it.
I still think a send_templated_email
, makes a lot of sense, but I've not had time to revisit this yet and propose something simpler.
I'll take it to django-developers if and when that happens.
If anyone else takes this to the group first, please link to the discussion from this ticket.
comment:17 by , 13 years ago
Cc: | added |
---|
comment:18 by , 13 years ago
Cc: | added |
---|
Hi there!
Replying to julien:
I do agree that creating html+plain emails is still a little too hard in Django, but in my opinion this could be simplified by introducing a new helper class wrapping around
EmailMultiAlternatives
and providing a simpler API and sensible defaults. For example, something like:HTMLEmailMessage(body_plain='...', body_html='...')
My implementation is based on EmailMultiAlternatives
:) It's available at GitHub https://github.com/artemrizhov/django-mail-templated
Actuall, for me it
I've emailed to django-developers mailing list and proposed to merge it into Django. However my proposition was declined. http://groups.google.com/group/django-developers/browse_frm/thread/4728f20dd8023dd9
I'd be glad to continue work on it, but there is no any reason to polish it more for a while. :( At least unless I need some additional functionality, or some activity is registered on the github project. :) Propositions and suggestions are welcome.
comment:20 by , 12 years ago
Cc: | added |
---|
comment:21 by , 12 years ago
Needs tests: | set |
---|---|
Patch needs improvement: | set |
Triage Stage: | Design decision needed → Accepted |
Rendering a template is simple, but there are some minor gotchas in templated email (like remembering to remove newlines from the rendered subject template) that would be reasonable to put in a convenience function in Django rather than requiring everyone to implement them independently. And the EmailMultiAlternatives
API is just enough boilerplate to always require looking up in the docs.
IMO the blocks approach implemented above (both in the patch and the linked external project) is too complex, too much code, and too unusual a use of templates (blocks are for template inheritance, not a way to combine multiple templates in one file.) I would rather favor a simple extension naming convention (i.e. give path/to/myemail
and it looks for templates path/to/myemail.txt
, path/to/myemail.html
, and path/to/myemail.subject.txt
). I've attached a sample implementation of this approach (not yet integrated as a patch to Django); it really isn't very much code. (This implements a two-layer API, with both a send_multipart
function that accepts strings, and a send_templated_multipart
that does the template-finding and rendering.)
I guess I'm personally +0 or +0.5 on including something like this in Django. I think it's a very common pattern that's just irritating enough to re-implement to be worth considering for inclusion, but I won't mind at all if another core dev decides to close this wontfix.
by , 12 years ago
Simple ex ampleimplementation (would need integration as Django patch, tests & docs)
comment:22 by , 12 years ago
Needs documentation: | set |
---|
comment:23 by , 12 years ago
too unusual a use of templates (blocks are for template inheritance, not a way to combine multiple templates in one file.)
Maybe you are right. However 3 files per message is too many. It may be hard and annoying to maintain so many files. And somebody may forget to change one of templates.
And why you say "multiple templates"? Imho this is single template of multipart message :) Let's add this at the top of template:
{% extends 'email/multipart.html' %}
Does it look better now? ;) We also may add 'email/text.html', 'email/html.html' and so on. This will make the template code looking more usual.
comment:24 by , 12 years ago
If the implementation requires Python code pulling out the blocks from the rendered template for individual specialized handling, then IMO it's a misuse of template blocks and it should be separate templates instead. If the hypothetical email/multipart.html
base actually contained a full template for a multipart message, that would be a reasonable use of templates (but still not necessarily a good idea, as the template would then be re-implementing things already handled by EmailMultiAlternatives
).
I don't buy the idea that maintaining three files is too hard. But for people who feel that way, alternative approaches would always be available as third-party packages. (Or we can just wontfix this and let all implementations stay outside core, I'm fine with that too.)
comment:25 by , 12 years ago
Or we can just wontfix this
Ok, you won :)
I hope it is possible to make both html and text vesrions not required.
comment:26 by , 11 years ago
Resolution: | → duplicate |
---|---|
Status: | assigned → closed |
Marking this as a duplicate of #20817 which added an html_message
parameter to send_mail()
.
comment:28 by , 11 years ago
@anon yes, you could use the render_to_string function to build the html_message
kwarg passed to send_mail
.
comment:29 by , 11 years ago
Resolution: | duplicate |
---|---|
Status: | closed → new |
No, this does not close the original OPs request, and concept as mentioned by Carl.
This ticket is to introduce a helper function of some sort to handle send_mail(render_to_string(...))
. It's just a helper function, it's achievable without, but it's an exceedingly common pattern and personally I'm +1 on having this. A significant number of projects I've worked on have included a utility function named something like render_to_send_mail()
.
The exact API is up for discussion still, which is probably the blocker for this ticket. As mentioned in #20817, it may be better as extension around the EmailMessage
or EmailMultiAlternatives
APIs rather than just as a helper function. I can see something like the following API being useful:
message = TemplatedEmail( from_email=..., to=[...], subject_template='my_email_subject.txt', body_template='my_email_template.txt', html_body_template='my_enhanced_email_template.html', context=context, ) message.send()
A functional approach to this could be added as well as a shortcut function, though this is already significantly easier than the existing APIs to do this. If we take Carl's earlier suggestion about a naming convention, it becomes simpler, alternatively we could add complexity by also supporting kwargs for subject
, body
and html_body
which can take rendered strings as alternatives.
comment:30 by , 10 years ago
Cc: | added |
---|
comment:31 by , 10 years ago
Description: | modified (diff) |
---|
comment:32 by , 10 years ago
With the addition of the html_message
parameter to send_mail
, the reasons for adding this have been reduced to:
- Saving the need to type
render_to_string
two or three times. - Saving the need to collapse newlines in the rendered subject (though I'm not actually sure off-hand what the consequence of failing to do that is, would need to check).
That starts to look a bit thin as rationale, but it is a common enough need that I'd probably still be at least +0 on adding it.
I think the naming-convention suggestion is probably a bit too implicit for Django's usual API style; that's something probably better left to a third-party solution.
So I think if we do this, the API should probably look something like what Marc proposed.
comment:33 by , 10 years ago
Also, one more argument against the "single template and pull out blocks" suggestion -- a single file with some portions that are HTML and some portions that are text does not work well with editor modes and syntax highlighting.
comment:34 by , 3 years ago
Just in case someone wants to implement a template rendering system for the email backend, I have added a templated based email renderer to https://github.com/ui/django-post_office
comment:35 by , 4 months ago
There are a number of third-party Django packages that provide templated email, with varying approaches for converting templates to email (and in varying states of maintenance):
- https://pypi.org/project/emark/
- https://pypi.org/project/django-pony-express/
- https://pypi.org/project/django-mail-templated/
- https://pypi.org/project/django-mail-templated-simple/
- https://pypi.org/project/django-post-office/
- https://pypi.org/project/django-templated-mail/
[I'm updating this comment periodically as I come across other libraries. Email templating seems to be a popular area for exploration.]
Yes, I think Django could provide this feature.