2

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

Alex O
  • 357
  • 3
  • 14

3 Answers3

2

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

Community
  • 1
  • 1
dani herrera
  • 48,760
  • 8
  • 117
  • 177
2

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)
Mohamed ElKalioby
  • 1,908
  • 1
  • 12
  • 13
2

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 = "myapperrorsender@gmail.com"
        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

Mark
  • 1,285
  • 1
  • 19
  • 28