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 settings.py
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 in settings.py.
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:
- EMAIL_USE_TLS or EMAIL_USE_SSL
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 settings.py file.
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.
from django.core.mail import send_mail()
The 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:
- subject: string
- message: string
- from_email: string
- recipient_list: list of strings
Worth noting: 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 protected]' recipient_list = ['[email protected]', ] # 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!
from django.template.loader import render_to_string()
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 html body 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 protected]' recipient_list = ['[email protected]', ] # 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 HttpResponse object.
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 BadHeaderError that 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 views.py file…
# 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!