Рекомендация постов по электронной почте

⏱ 25 мин · 🧩 заданий: 5

Задача

Добавим под постом ссылку «Поделиться»: читатель заполняет форму (своё имя, 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

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Написать код

Создайте форму EmailPostForm (forms.Form): name — строка до 25 символов, email и to — EmailField, comments — необязательная строка с виджетом Textarea.

Задание 2 Написать код

Отправьте письмо функцией send_mail: тема — переменная subject, текст — message, отправитель — None (будет взят DEFAULT_FROM_EMAIL), получатели — список из cd["to"].

Задание 3 Один вариант ответа

Какой бэкенд почты удобен при разработке, чтобы письма не уходили, а печатались в консоль?

Задание 4 Один вариант ответа

Как получить полный адрес поста (с доменом) для письма?

Задание 5 Несколько вариантов

Какие данные нельзя хранить прямо в settings.py, который попадает в git?

Войдите, чтобы отмечать пройденные уроки.