Отправка email во Flask

Многие веб-приложения в той или иной степени связаны с отправкой электронных писем. Поэтому необходимо научиться делать это посредством Flask. 

Если говорить о стандартном функционале Python, там есть модуль smtplib, который отвечает за отправку сообщений. Несмотря на то, что вовсе не сложный для понимания, он все же требует определенных навыков взаимодействия с ним, а также времени. Чтобы сделать процесс отправки электронных писем проще, используется расширение Flask-Mail. Он основан на приведенном выше модуле, но значительно упрощает отправку электронных писем. Помимо этого, в его функционал входит массовая рассылка и файлы, прикрепленные во вложении.

Чтобы инсталлировать Flask-Mail, используется такая команда. 

(env) gvido@vm:~/flask_app$ pip install flask-mail

Для запуска расширения, необходимо осуществить импорт класса Mail из пакета flask_mail и создать элемент соответствующего класса (то бишь, Mail). 

#...

from flask_mail import Mail, Message




app = Flask(__name__)

app.debug = True

app.config['SECRET_KEY'] = 'a really really really really long secret key'

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:pass@localhost/flask_app_db'




manager = Manager(app)

manager.add_command('db', MigrateCommand)

db = SQLAlchemy(app)

migrate = Migrate(app,  db)

mail = Mail(app)

#...

Затем необходимо ввести ряд параметров настройки, чтобы Flask-Mail знал SMTP-сервер, к которому надо подключаться. Чтобы это сделать, необходимо в файле main2.py ввести такой код. 

#...

app.config['SECRET_KEY'] = 'a really really really really long secret key'

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:pass@localhost/flask_app_db'

app.config['MAIL_SERVER'] = 'smtp.googlemail.com'

app.config['MAIL_PORT'] = 587

app.config['MAIL_USE_TLS'] = True

app.config['MAIL_USERNAME'] = 'test@gmail.com'  # введите свой адрес электронной почты здесь

app.config['MAIL_DEFAULT_SENDER'] = 'test@gmail.com'  # и здесь

app.config['MAIL_PASSWORD'] = 'password'  # введите пароль




manager = Manager(app)

manager.add_command('db', MigrateCommand)

db = SQLAlchemy(app)

mail = Mail(app)

#...

В этом случае применяется сервер от компании Google. Необходимо учитывать то, что максимальное количество сообщений, которые эта компания разрешает отправлять в день – 150. Если этого недостаточно, есть специальные сервисы, которые осуществляют отправку рассылок. К таковым относятся SendGrid или MailChimp.

Вместо того, чтобы напрямую вводить электронный адрес и пароль в приложении, лучше, чтобы они находились в переменных среды. В этом случае, при изменении почты или пароля, нет необходимости вносить в код какие-либо изменения. 

Flask-Mail: основы

Чтобы составить электронное письмо, используется объект класса Message

msg = Message("Subject", sender="sender@example.com",  recipients=['recipient_1@example.com'])

Если в ходе настройки параметров конфигурации MAIL_DEFAULT_SENDER указывается, то тогда нет необходимости передавать значение sender при создании Message

msg = Message("Subject", recipients=['recipient@example.com'])

Чтобы задать содержимое письма, используется атрибут body объекта Message. 

msg.body = "Mail body"

Некоторые электронные письма пишутся не в виде текста, а в виде html-кода. В этом случае используется специальный атрибут html. 

msg.html = "<p>Mail body</p>"

А для того, чтобы отправить сообщение, необходимо передать соответствующий элемент методу send() экземпляра Mail. 

mail.send(msg)

Теперь необходимо разобраться в настройках, отправив email с использованием командной строки.

Отправка тестового письма

Теперь давайте посмотрим, как отправлять письма, на практике. Открываем терминал и вводим такие инструкции. 

(env) overiq@vm:~/flask_app$ python main2.py shell

>>>

>>> from  main2 import  mail,  Message

>>> # введите свою почту

>>> msg = Message("Subject", recipients=["you@mail.com"])

>>> msg.html = "<h2>Email Heading</h2>\n<p>Email Body</p>"

>>>

>>> mail.send(msg)

>>>

В случае успеха операции, на почту должно прийти электронной письмо. Тема будет Subject, а содержимое следующее:

Email Heading

Email Body

Необходимо учесть, что отправка через SMTP-сервер Gmail не будет работать в случае включенной двухфакторной аутентификации. Также необходимо разрешить небезопасным приложениям получать доступ к аккаунту. Эти системы защиты были предусмотрены, чтобы защитить пользователей, но конкретно в данной ситуации они больше мешают.

Особенности интеграции функционала по работе с электронными письмами в программу

Сейчас, когда пользователь отправляет обратную связь, она автоматически сохраняется в базу данных. В свою очередь, пользователь получает сообщение о том, что письмо было отправлено успешно. Тем не менее, программа должна сообщать администратору о том, что обратная связь была получена. По крайней мере, это – идеальный вариант. 

И реализовать эту задачу с помощью Flask возможно. 

Давайте снова откроем main2.py, чтобы внести коррективы в функцию представления contact() таким образом, чтобы она совершала отсылку этого уведомления. 

#...

@app.route('/contact/', methods=['get', 'post'])

def contact():

    #...

db.session.commit()

msg = Message("Feedback", recipients=[app.config['MAIL_USERNAME']])

msg.body = "You have received a new feedback from {} <{}>.".format(name, email)

mail.send(msg)




print("\nData received. Now redirecting ...")

    #...

Затем необходимо осуществить запуск сервера и зайти на http://locahost:5000/contact/. Заполним и отправим форму. В случае успеха при совершении этой операции на адрес электронной почты должно прийти письмо.

Можно было обратить внимание на задержку между уведомлением и отправкой электронного письма. Проблема здесь в том, что метод mail.send() осуществляет блокировку функции представления. Конечно, это ненадолго, всего на пару секунд. Тем не менее, код с перенаправлением страницы не будет исполняться до возвращения этого метода. 

Как решить эту проблему? Например, использовать потоки. 

Помимо этого, можно немного откорректировать код отправки сообщений. На данный момент в случае необходимости отправки электронного письма в любом другом месте кода, необходимо копировать и вставлять строки. Тем не менее, разработчику всегда доступна возможность сохранить несколько из них, заключив логику отправки сообщений в функцию.

Давайте откроем файл main2.py, чтобы перед index вставить следующий фрагмент. 

#...

from threading import Thread

#...

def shell_context():

    import os, sys

    return dict(app=app, os=os, sys=sys)




manager.add_command("shell",  Shell(make_context=shell_context))




def async_send_mail(app, msg):

    with app.app_context():

mail.send(msg)





def send_mail(subject, recipient, template, **kwargs):

    msg = Message(subject,      sender=app.config['MAIL_DEFAULT_SENDER'],  recipients=[recipient])

    msg.html = render_template(template,  **kwargs)

    thr = Thread(target=async_send_mail,  args=[app,  msg])

    thr.start()

    return thr




@app.route('/')

def index():

    return render_template('index.html', name='Jerry')

#...

Здесь было внесено несколько коррективов. Функция send_mail() теперь включает всю логику отправления электронного письма. Она получает как тему письма, так и адресата, а также шаблон самого сообщения. Помимо этого, она может принимать еще некоторые аргументы-ключевые слова. Почему именно в форме ключевых слов? Все потому, что дополнительные аргументы представляют собой данные, для которых требуется передача шаблону. На 17 строке осуществляется его рендеринг, после чего результат передается атрибуту msg.html.

На строке 18 создается объект Thread. Это реализуется путем передачи названия функции и ее аргументов. 

Следующая строка запускает потоки, и при этом вызывается async_send_mail()

Интересно то, что в коде вся работа выполняется в новом потоке, то есть, за рамками приложения. Если говорить языком программирования, за пределами функции представления, в отдельном потоке.

Далее создается контекст приложения. Это делается путем использования with app.app_context(): А отправку электронного письма осуществляет mail.send().

Затем необходимо сгенерировать шаблон для уведомления обратной связи. Для этого необходимо перейти в папку templates и там создать каталог mail, в котором и будут храниться шаблоны для писем. Внутри этой папки мы создаем шаблон с названием feedback.html, в котором содержится следующий код. 

<p>You have received a new feedback from {{ name }} &lt;{{ email }}&gt; </p>

Затем вносятся коррективы в функцию представления contact() для использования функции send_mail(). Затем осуществляется повторный заход на адрес сервера, по которому размещается наше приложение, заполняется форма и осуществляется ее дальнейшая отправка. В этом случае задержки не будет.

Выводы

Таким образом, реализовать работу с электронными письмами в Python можно значительно легче с помощью специального расширения Flask. Ситуаций, в которых могут понадобиться эти знания, огромное количество. Почти все веб-приложения в той или иной степени работают с электронной почтой, даже если они напрямую не предназначены для этого.

Допустим, банк создал депозитный калькулятор, после чего отправляет человеку, который подбирал депозит для себя рекламу с целью, чтобы уже разогретый клиент воспользовался их услугами. Есть множество других применений, для которых может понадобиться e-mail.

ОфисГуру
Adblock
detector