1

I'm trying to set up my Django account to receive the Error reporting (docs here).

I have added ADMINS to my settings.py. Then, as per the documentation:

In order to send email, Django requires a few settings telling it how to connect to your mail server. At the very least, you’ll need to specify EMAIL_HOST and possibly EMAIL_HOST_USER and EMAIL_HOST_PASSWORD, though other settings may be also required depending on your mail server’s configuration. Consult the Django settings documentation for a full list of email-related settings.

But here is when I get lost. I have a Business Gmail account, which is the one I would like to link here. This post was explaining it wonderfully,

EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'myemail@mydomain.com'
EMAIL_HOST_PASSWORD = 'mypassword'

but it says:

In 2016 Gmail is not allowing this anymore.

Apparently, the problem is in the EMAIL_HOST_PASSWORD setting, which has to be an specific password, as noted in this other post.

However, it is hard to believe that Gmail does not allow that in any way, especially with a Business account where you are paying money for the service.

Unfortunately, all related info I found is older than 2016 and therefore not useful anymore.

Is there a way to connect the Django app with Gmail?

Community
  • 1
  • 1
J0ANMM
  • 7,849
  • 10
  • 56
  • 90
  • Have you turned on access for less secure apps? https://support.google.com/accounts/answer/6010255 – voodoo-burger Feb 09 '17 at 11:00
  • Yes. This point is described in the explanations I followed and I was able to do it. However, I would prefer to keep the two-step verification: `This setting is not available for accounts with 2-Step Verification enabled. Such accounts require an application-specific password for less secure apps access.` – J0ANMM Feb 09 '17 at 12:26
  • so what's the problem with using an app-specific password? – voodoo-burger Feb 09 '17 at 13:39
  • That I cannot get it, as explained in the comments of [this answer](http://stackoverflow.com/a/23402208/5802289) – J0ANMM Feb 09 '17 at 14:24
  • @J0ANNM I don't think there's any other choice then. I know I always have to enable less secure access any time I want to use my google apps accounts from Django. – voodoo-burger Feb 10 '17 at 11:45
  • Interestingly, as of August 2021, app-specific passwords are only available for accounts with two-factor auth. – BenB Aug 17 '21 at 01:36

3 Answers3

0

The work-around that eventually worked for me was to create a new Gmail account just for this purpose. This works for the moment, despite some comments I read somewhere else saying the contrary.

Note that this new account will be without two-step verification, but security is not such a big concern as the account will "only" deal with Django emails.

J0ANMM
  • 7,849
  • 10
  • 56
  • 90
0

You can use the Gmail API to send authorized emails through a Gmail email address. A good place to start is the documentation: https://developers.google.com/gmail/api/quickstart/python

I've run into this problem consistently, so I documented how to use the API a blog post: https://www.willcarh.art/blog/Automating-Emails-in-Python/

It was such a pain that I ended up building my own Python utility for sending emails via the Gmail API. This was my initial prototype:

import os
import sys
import pickle
import base64
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from email.mime.text import MIMEText

def get_gmail_api_instance():
    """
    Setup Gmail API instance
    """
    if not os.path.exists('token.pickle'):
        return None
    with open('token.pickle', 'rb') as token:
        creds = pickle.load(token)
    service = build('gmail', 'v1', credentials=creds)
    return service

def create_message(sender, to, subject, message_text):
    """
    Create a message for an email
        :sender: (str) the email address of the sender
        :to: (str) the email address of the receiver
        :subject: (str) the subject of the email
        :message_text: (str) the content of the email
    """
    message = MIMEText(message_text)
    message['to'] = to
    message['from'] = sender
    message['subject'] = subject
    raw = base64.urlsafe_b64encode(message.as_bytes())
    raw = raw.decode()
    body = {'raw': raw}
    return body

def send_email(service, user_id, message):
    """
    Send an email via Gmail API
        :service: (googleapiclient.discovery.Resource) authorized Gmail API service instance
        :user_id: (str) sender's email address, used for special "me" value (authenticated Gmail account)
        :message: (base64) message to be sent
    """
    try:
        message = (service.users().messages().send(userId=user_id, body=message).execute())
        return message
    except Exception as e:
        print("err: problem sending email")
        print(e)

def main():
    """
    Set up Gmail API instance, use it to send an email
      'sender' is the Gmail address that is authenticated by the Gmail API
      'receiver' is the receiver's email address
      'subject' is the subject of our email
      'message_text' is the content of the email
    """
    # draft our message
    sender = 'pythonista@gmail.com'
    receiver = 'receiver@gmail.com'
    subject = 'Just checking in!'
    message_text = "Hi! How's it going?"

    # authenticate with Gmail API
    service = get_gmail_api_instance()
    if service == None:
        print("err: no credentials .pickle file found")
        sys.exit(1)

    # create message structure
    message = create_message(sender, receiver, subject, message_text)

    # send email
    result = send_email(service, sender, message)
    if not result == None:
        print(f"Message sent successfully! Message id: {result['id']}")

if __name__ == '__main__':
    main()

Then, to get Django to send emails on 404, 500, etc. errors, add to relevant urls.py:

from django.conf.urls import handler404, handler500
handler404 = projectname_views.error_404
handler500 = projectname_views.error_500

And in relevant views.py, add:

import send_gmail
from django.shortcuts import render

def error_500(request):
    # call email function
    send_gmail.main()
    response = render(request, '500_errror_template.html')
    response.status_code = 500
    return response

GitHub gist with the code above: https://gist.github.com/wcarhart/b4f509c46ad1515a9954d356aaf10df1

wcarhart
  • 2,685
  • 1
  • 23
  • 44
0

I wanted to provide an update as of August 2021.

I have this working on a business gmail account using only the libraries included with Django.

In settings.py

EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'myname@mydomain.com'
EMAIL_HOST_PASSWORD = 'myappspecificpassword'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER

For the password, you need to generate an app-specific password for the account in question. You can do that by visiting this link: https://security.google.com/settings/u/3/security/apppasswords.

Note: You must have 2-factor authentication enabled for the account you're sending from. This must be enabled through the account you're sending from -- not the admin account.

Doing that, I can send an email using this snippet in a view in views.py

        from django.core.mail import EmailMessage
        ...


        email = EmailMessage(
        'Hello',
        'Body goes here',
        'bob@example.com',
        ['user@mydomain.com'],
        ['bcc@example.com'],
        reply_to=['myuser@mydomain.com'],
        headers={'Message-ID': 'foo'},
        )
        
        email.send()

BenB
  • 1,010
  • 12
  • 17