I’m looking to make my app notify the user when their object is updated by an admin.
Problem is that I had no idea how to use my own email templates.
But I got it working! Here’s how you can put it into your Django project.
I’m using Django 2.2.5.
Email settings in
There are two useful ways that I know of to send emails:
- Logging directly to the console window
- Sending via SMTP
The first is much quicker, but doesn’t actually send emails. It’s good for testing.
The second is what you need if you need to actually send emails to a user’s inbox.
Send to console
To log emails to the console, you just have to set
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
Send via SMTP
There are a few settings you’ll need to take care of if you want to send live SMTP emails:
If you’re ready to give this a try, I would recommend making an account on SendGrid. Even when your free trial expires, you’ll have 100 free emails to send every day. Not enough to run a big project, but plenty of leeway to learn how to use it.
Once you’re signed up, you want to find the “Send Your First Email” instructions to set up SMTP.
Then choose SMTP relay, not Web API.
Then you need to create a SendGrid API key. This will be your
EMAIL_HOST_PASSWORD in your
After naming and creating your key, step two will autopopulate. These are what you’ll use in your
settings.py file. Here’s a link to the SendGrid documentation on exactly how to set it up.
Make sure all the settings outlined at the beginning of this section are set, then you can try to actually send an email.
send_mail() function has been in Django for a while now. It takes a few parameters and sends an email. Very simple.
You must supply:
recipient_list: list of strings
message is the plain text body content of the email. If you want to send an HTML email, you want to set
html_message. We’ll do both to get some practice with templates.
Sending a plain email
Let’s practice. I’m assuming you have your
EMAIL_BACKEND all set up. If you can’t get SMTP working, make it log to console to get the templates working first. It’s much more encouraging than sitting there trying to troubleshoot why SMTP isn’t sending.
# settings.py EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
So for my app, I want to send an email when my
UpdateView form is submitted. This will notify the user related to that model object that they should come check it out. So I will override the
form_valid() method to send an email when the
UpdateView form is submitted.
# app_name/views.py from django.core.mail import send_mail from django.views.generic import UpdateView from .models import ModelName class ModelNameUpdateView(UpdateView): model = ModelName fields = ['field_1', 'field_2',] template_name = 'app_name/model_name_update.html' def form_valid(self, form): if request.method.POST: from_email = 'email@example.com' recipient_list = ['firstname.lastname@example.org',] # put your real email here subject = 'Subject text' message = 'This is the body of the email.' send_mail(subject, message, from_email, recipient_list) return super(ModelNameUpdateView, self).form_valid(form)
Update your model with your
UpdateView and check your console or inbox (depending on your
EMAIL_BACKEND) to see if it worked!
Now that emails are working, we can discuss the templates. Email templates work just like your traditional Django templates that load pages: we put the content in its own file, then we point to that file.
We’re going to make three templates:
Make those three files in their respective folders, then move your strings from
views.py to each respective template.
This is the body of the email.
This is the <strong>html body</strong> of the email.
Now we move back to
views.py where we want to point to these template files.
# app_name/views.py from django.template.loader import render_to_string # new from django.core.mail import send_mail from django.views.generic import UpdateView from .models import ModelName class ModelNameUpdateView(UpdateView): model = ModelName fields = ['field_1', 'field_2',] template_name = 'app_name/model_name_update.html' def form_valid(self, form): if request.method.POST: from_email = 'email@example.com' recipient_list = ['firstname.lastname@example.org',] # put your real email here subject = render_to_string( template_name='app_name/email/model_name_update_email_subject.txt' ) message = render_to_string( template_name='app_name/email/model_name_update_email_message.txt' ) html_message = render_to_string( template_name='app_name/email/model_name_update_email_message.html' ) send_mail(subject, message, from_email, recipient_list) return super(ModelNameUpdateView, self).form_valid(form)
render_to_string() is kind of like the
render() function you may have used before, but returns the template as a string instead of an
Give that a shot and see if it works… hint: it probably won’t.
Python strip() function
I got a variety of errors here.
- When using the console backend, I just didn’t see any email at all.
- When using the SMTP backend, I received a
BadHeaderErrorthat says “Header values can’t contain newlines”.
You might be thinking “I don’t want any newlines in there!”
Well, we have a solution: strip the whitespace off the ends with the python
strip() function. Update your
# app_name/views.py subject = render_to_string( template_name='app_name/email/model_name_update_email_subject.txt' ).strip() # new
And try to trigger your email again.
And it should be successful!