Reputation: 367
There are SMTP Sendgrid settings in my settings.py file for user-notification purposes:
EMAIL_HOST = 'smtp.sendgrid.net'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'my_username'
EMAIL_HOST_PASSWORD = 'my_password'
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = 'my_from_email'
It works good, but the problem is that I want to use different SMTP server for the built-in Internal Server Errors notification system, which is auto-enabled when DEBUG = False and ADMINS tuple is not empty.
How could it be achieved?
Thanks in advance
Upvotes: 2
Views: 1514
Reputation: 1295
Tweaking @dani-herrera's answer, this worked for me in Django 1.11: modify the connection
property instead of the __init__
.
All together, the full code is as follows.
In settings.py
:
# For transactional emails:
SENDGRID_API_KEY = "SG.1234ABCD"
EMAIL_HOST = "smtp.sendgrid.net"
EMAIL_HOST_USER='apikey'
EMAIL_HOST_PASSWORD=SENDGRID_API_KEY
# ... ^that's used for all emails EXCEPT server error alerts.
# Change one line in LOGGING, to use custom awesomeAdminEmailHandler for server emails:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': { 'class': 'logging.StreamHandler' },
'mail_admins': {
'level': 'ERROR',
# remove this default: 'class': 'django.utils.log.AdminEmailHandler',
# replace with custom smtp login for ERROR emails:
'class': 'myapp.email.adminsender.awesomeAdminEmailHandler', # yes, in quotes
# 'include_html': False, # The include_html argument of AdminEmailHandler is used to control whether the traceback email includes an HTML attachment containing the full content of the debug Web page that would have been produced if DEBUG were True. This information is potentially very sensitive, and you may not want to send it over email.
}
},
'loggers': {
# [Don't change anything]
...
...
}
}
Then in myapp/email/adminsender.py
file:
from django.utils.log import AdminEmailHandler
from django.core.mail import get_connection
class awesomeAdminEmailHandler( AdminEmailHandler ):
"""
In this example, we create a special Gmail account just for sending error emails.
Code inspired by https://stackoverflow.com/a/33010978/870121
"""
def connection(self):
my_host = "smtp.gmail.com"
my_port = 587
my_username = "[email protected]"
my_password = "abcd1234"
my_use_tls = True
return get_connection(backend=self.email_backend,
host=my_host,
port=my_port,
username=my_username,
password=my_password,
use_tls=my_use_tls)
Motivation for bothering to do this:
If we send ALL emails through Sendgrid, i.e., transactional app (user) emails and server error emails, that's not ideal.
One server error might cause ~100s of emails to be sent in 2 minutes.
Most of these emails won't be opened.
This low open rate might make your dedicated IP address look bad (if you have one), and/or hurt the reputation of our sending domain (myapp.com) too? Or, you might find it uses up your monthly limit of # of emails sent.
So, preserve 'myapp.com' for important user-facing emails, and use this awesomeAdminEmailHandler
for sending 5xx error emails from a different email account.
Note on Gmail as in this example
Gmail is a great idea for testing, but probably not a good idea for use in production.
SMTP will work as in the code snippet above iff you first allow "insecure apps", https://myaccount.google.com/lesssecureapps
Upvotes: 2
Reputation: 2334
The code isn't 100% accurate with Django 1.8
from django.utils.log import AdminEmailHandler
from django.core.mail import get_connection
from django.conf import settings
from django.core.mail.backends.smtp import EmailBackend
from django.core.mail.message import EmailMultiAlternatives
class SMTPConnection (EmailBackend):
def __call__(self, *args, **kwargs):
self.host = settings.ADMINS_SMTP["HOST"]
self.port = settings.ADMINS_SMTP["PORT"]
self.username = settings.ADMINS_SMTP["USERNAME"]
self.password = settings.ADMINS_SMTP["PASSWORD"]
self.use_tls = settings.ADMINS_SMTP["USE_TLS"]
return self
class EmailAdmins( AdminEmailHandler ):
def __init__(self, include_html=False, email_backend=None):
self.include_html=include_html
AdminEmailHandler.__init__(self, include_html, email_backend)
self.connection = SMTPConnection(host=settings.ADMINS_SMTP["HOST"],port=settings.ADMINS_SMTP["PORT"],username=settings.ADMINS_SMTP["USERNAME"],password=settings.ADMINS_SMTP["PASSWORD"],use_tls=settings.ADMINS_SMTP["USE_TLS"])
def send_mail(self, subject, message, *args, **kwargs):
if not settings.ADMINS:
return
mail = EmailMultiAlternatives(subject='%s%s' % (settings.EMAIL_SUBJECT_PREFIX, subject),
body=message, from_email=settings.ADMINS_SMTP["USERNAME"], to=[a[1] for a in settings.ADMINS],
connection=self.connection,)
mail.send(fail_silently=True)
Upvotes: 2
Reputation: 51655
You can set a different error handler on settings:
'handlers': {
'null': {
'level': 'DEBUG',
'class': 'logging.NullHandler',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'mail_admins': {
'level': 'ERROR',
'class': 'myAdminEmailHandler',
'filters': ['special']
}
And overwrite connection there:
from django.utils.log import AdminEmailHandler
from django.core.mail import get_connection
class myAdminEmailHandler( AdminEmailHandler ):
def __init__(self, include_html=False, email_backend=None):
AdminEmailHandler.__init__(self,include_html, email_backend)
self.my_host = ''
self.my_port = 587
self.my_username = ''
self.my_password = ''
self.my_use_tls = True
self.connection = get_connection(host=my_host,
port=my_port,
username=my_username,
password=my_password,
use_tls=my_use_tls)
Disclaimer, not tested. Some credits to @Daniel Backman: https://stackoverflow.com/a/14398138
Upvotes: 2