Рекомендация постов по электронной почте
Задача
Добавим под постом ссылку «Поделиться»: читатель заполняет форму (своё имя, email, email друга, комментарий), а сайт отправляет другу письмо со ссылкой на пост.
Форма
blog/forms.py:
from django import forms
class EmailPostForm(forms.Form):
name = forms.CharField(label="Ваше имя", max_length=25)
email = forms.EmailField(label="Ваш email")
to = forms.EmailField(label="Email получателя")
comments = forms.CharField(label="Комментарий", required=False, widget=forms.Textarea)
Это обычная Form, не ModelForm: данные не сохраняются в базу.
View
from django.core.mail import send_mail
from django.shortcuts import get_object_or_404, render
from .forms import EmailPostForm
from .models import Post
def post_share(request, post_id):
post = get_object_or_404(Post, id=post_id, status=Post.Status.PUBLISHED)
sent = False
if request.method == "POST":
form = EmailPostForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
post_url = request.build_absolute_uri(post.get_absolute_url())
subject = f"{cd['name']} рекомендует прочитать «{post.title}»"
message = (
f"Прочитайте «{post.title}»: {post_url}\n\n"
f"Комментарий от {cd['name']}: {cd['comments']}"
)
send_mail(subject, message, None, [cd["to"]])
sent = True
else:
form = EmailPostForm()
return render(request, "blog/post/share.html", {"post": post, "form": form, "sent": sent})
request.build_absolute_uri()превращает путь в полный адрес с протоколом и доменом.send_mail(subject, message, from_email, recipient_list):from_email=None— взятьDEFAULT_FROM_EMAILиз настроек.- Флаг
sentпозволяет показать в шаблоне сообщение об успехе.
Маршрут:
path("<int:post_id>/share/", views.post_share, name="post_share"),
Шаблон
blog/post/share.html:
{% extends "blog/base.html" %}
{% block title %}Поделиться постом{% endblock %}
{% block content %}
{% if sent %}
<h1>Письмо отправлено</h1>
<p>«{{ post.title }}» успешно отправлен на {{ form.cleaned_data.to }}.</p>
{% else %}
<h1>Поделиться «{{ post.title }}»</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Отправить">
</form>
{% endif %}
{% endblock %}
И ссылка в detail.html:
<p><a href="{% url 'blog:post_share' post.id %}">Поделиться постом</a></p>
Настройка почты
При разработке
Чтобы не настраивать почтовый сервер, выводите письма в консоль:
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
DEFAULT_FROM_EMAIL = "Мой блог <noreply@myblog.ru>"
Отправьте форму — письмо появится в терминале, где запущен runserver.
Реальная отправка через SMTP
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.yandex.ru" # или smtp.gmail.com, smtp.mail.ru
EMAIL_PORT = 465
EMAIL_USE_SSL = True
EMAIL_HOST_USER = "you@yandex.ru"
EMAIL_HOST_PASSWORD = os.environ["EMAIL_PASSWORD"] # пароль приложения
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
Почтовые сервисы требуют создать пароль приложения в настройках безопасности аккаунта — обычный пароль не подойдёт.
Секреты — не в git
Пароли, SECRET_KEY и ключи API не должны попадать в репозиторий. Храните их в переменных окружения.
Удобно использовать файл .env (добавленный в .gitignore) и пакет python-decouple или django-environ:
from decouple import config
EMAIL_HOST_PASSWORD = config("EMAIL_HOST_PASSWORD")
Проверка в shell
>>> from django.core.mail import send_mail
>>> send_mail("Тест", "Работает!", None, ["friend@example.com"])
1
Возвращает количество успешно отправленных писем.
Письма в HTML
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
html = render_to_string("blog/email/share.html", {"post": post, "url": post_url})
msg = EmailMultiAlternatives(subject, message, None, [cd["to"]])
msg.attach_alternative(html, "text/html")
msg.send()
Итог
- Форма на
forms.Form+ view с GET/POST — знакомый шаблон. send_mail()отправляет письмо,build_absolute_uri()строит полную ссылку.- В разработке —
console.EmailBackend, в продакшене — SMTP с паролем приложения. - Секреты храните в переменных окружения.
Практика решено 0 из 5
Создайте форму EmailPostForm (forms.Form): name — строка до 25 символов, email и to — EmailField,
comments — необязательная строка с виджетом Textarea.
from django import forms
class EmailPostForm(forms.Form):
name = forms.CharField(max_length=25)
email = forms.EmailField()
to = forms.EmailField()
comments = forms.CharField(required=False, widget=forms.Textarea)
Отправьте письмо функцией send_mail: тема — переменная subject, текст — message,
отправитель — None (будет взят DEFAULT_FROM_EMAIL), получатели — список из cd["to"].
from django.core.mail import send_mail
send_mail(subject, message, None, [cd["to"]])
Какой бэкенд почты удобен при разработке, чтобы письма не уходили, а печатались в консоль?
Как получить полный адрес поста (с доменом) для письма?
Какие данные нельзя хранить прямо в settings.py, который попадает в git?
Войдите, чтобы отмечать пройденные уроки.