Django База [2023]: Добавление формы обратной связи #28
Данная статья подробно описывает процесс создания модели и формы для возможности пользователей и гостей отправлять сообщения администрации сайта используя форму обратной связи в Django.
Если вы хотите выразить благодарность автору сайта, статей и курса по Django, вы можете сделать это по ссылке ниже:
Чтобы отправить письмо вам необходимо настроить SMTP-Сервер, в одном из уроков всё подробно расписано.
Создание модели обратной связи
Первое, что нам необходимо - это создание модели обратной связи, чтобы мы могли просматривать данные в админ-панеле для большего удобства.
В нашем приложении system, в файле models.py добавим следующую модель:
from django.db import models
from django.contrib.auth.models import User
class Feedback(models.Model):
"""
Модель обратной связи
"""
subject = models.CharField(max_length=255, verbose_name='Тема письма')
email = models.EmailField(max_length=255, verbose_name='Электронный адрес (email)')
content = models.TextField(verbose_name='Содержимое письма')
time_create = models.DateTimeField(auto_now_add=True, verbose_name='Дата отправки')
ip_address = models.GenericIPAddressField(verbose_name='IP отправителя', blank=True, null=True)
user = models.ForeignKey(User, verbose_name='Пользователь', on_delete=models.CASCADE, null=True, blank=True)
class Meta:
verbose_name = 'Обратная связь'
verbose_name_plural = 'Обратная связь'
ordering = ['-time_create']
db_table = 'app_feedback'
def __str__(self):
return f'Вам письмо от {self.email}'
После этого нам необходимо провести миграции в базу данных с помощью команды python manage.py makemigrations
и python manage.py migrate
Результат миграций:
(venv) PS C:\Users\Razilator\Desktop\Base\backend> py manage.py makemigrations
Migrations for 'system':
modules\system\migrations\0004_feedback.py
- Create model Feedback
(venv) PS C:\Users\Razilator\Desktop\Base\backend> py manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions, sites, system
Running migrations:
Applying system.0004_feedback... OK
И переходим к созданию формы.
Создание формы для обратной связи
В файле forms.py нашего приложения system мы создадим форму на основе нашей модели.
class FeedbackCreateForm(forms.ModelForm):
"""
Форма отправки обратной связи
"""
class Meta:
model = Feedback
fields = ('subject', 'email', 'content')
def __init__(self, *args, **kwargs):
"""
Обновление стилей формы
"""
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs.update({'class': 'form-control', 'autocomplete': 'off'})
В примере выше мы создали форму на основе нашей модели Feedback, задав к показу 3 поля - тема, email адрес и содержимое. Остальные поля заполним во вьюхе.
Создание функции получения IP-Адреса и отправки письма
В одном из уроков мы создавали папку services, а в ней файл utils.py, в нём мы и создадим необходимый функционал:
def get_client_ip(request):
"""
Get user's IP
"""
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
ip = x_forwarded_for.split(',')[0] if x_forwarded_for else request.META.get('REMOTE_ADDR')
return ip
Функция get_client_ip()
получает request и из него извлекает данные об IP адресе.
Теперь в этой же папке services создадим файл email.py, здесь мы будем пополнять функции для наших писем, а в будущем сделаем их асинхронную работу. Добавим следующий код функции:
from django.core.mail import EmailMessage
from django.conf import settings
from django.template.loader import render_to_string
from django.contrib.auth.models import User
def send_contact_email_message(subject, email, content, ip, user_id):
"""
Function to send contact form email
"""
user = User.objects.get(id=user_id) if user_id else None
message = render_to_string('system/email/feedback_email_send.html', {
'email': email,
'content': content,
'ip': ip,
'user': user,
})
email = EmailMessage(subject, message, settings.EMAIL_SERVER, settings.EMAIL_ADMIN)
email.send(fail_silently=False)
Этот код представляет функцию send_contact_email_message()
для отправки электронного письма из формы обратной связи сайта. Функция принимает 5 аргументов: тему письма, адрес электронной почты, содержание письма, IP-адрес пользователя и идентификатор пользователя.
В функции сначала используется метод get для получения пользователя с указанным идентификатором из модели User, если идентификатор пользователя присутствует.
Затем используется шаблон письма system/email/feedback_email_send.html для генерации сообщения письма. В шаблон передаются данные электронной почты, содержания письма, IP-адреса пользователя и пользователя.
Далее создается объект EmailMessage с указанным темой, сообщением, адресом электронной почты сервера и адресом администратора, указанным в settings.
Создание представления для обратной связи
В файл views.py нашего приложения system добавим следующее представление CreateView:
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.views.generic import CreateView
from .forms import FeedbackCreateForm
from .models import Feedback
from ..services.email import send_contact_email_message
from ..services.utils import get_client_ip
class FeedbackCreateView(SuccessMessageMixin, CreateView):
model = Feedback
form_class = FeedbackCreateForm
success_message = 'Ваше письмо успешно отправлено администрации сайта'
template_name = 'system/feedback.html'
extra_context = {'title': 'Контактная форма'}
success_url = reverse_lazy('home')
def form_valid(self, form):
if form.is_valid():
feedback = form.save(commit=False)
feedback.ip_address = get_client_ip(self.request)
if self.request.user.is_authenticated:
feedback.user = self.request.user
send_contact_email_message(feedback.subject, feedback.email, feedback.content, feedback.ip_address, feedback.user_id)
return super().form_valid(form)
В примере выше мы используем представление на основе класса (CreateView), а тажке миксин (SuccessMessageMixin) для уведомления об успешной отправке письма.
В этом классе указаны модель (Feedback), форма (FeedbackCreateForm), шаблон (system/feedback.html), контекст для заголовка страницы (extra_context) адрес для успешной отправки (home) и сообщение об успехе.
Метод form_valid()
переопределяет метод родительского класса и вызывается после успешной валидации формы. В нем создается объект feedback, заполняется его атрибут ip_address с помощью функции get_client_ip()
, а если пользователь аутентифицирован, то заполняется его атрибут user. Затем вызывается функция send_contact_email_message()
, которая отправляет электронное письмо.
Обработка в urls.py
Данное представление нужно обработать, поэтому добавляем следующую строку в наш urls.py нашего приложения system:
from django.urls import path
from .views import ProfileUpdateView, ProfileDetailView, UserRegisterView, UserLoginView, UserPasswordChangeView, \
UserForgotPasswordView, UserPasswordResetConfirmView, UserConfirmEmailView, EmailConfirmationSentView, \
EmailConfirmedView, EmailConfirmationFailedView, UserLogoutView, FeedbackCreateView
urlpatterns = [
path('user/edit/', ProfileUpdateView.as_view(), name='profile_edit'),
path('user/<str:slug>/', ProfileDetailView.as_view(), name='profile_detail'),
path('login/', UserLoginView.as_view(), name='login'),
path('logout/', UserLogoutView.as_view(), name="logout"),
path('password-change/', UserPasswordChangeView.as_view(), name='password_change'),
path('password-reset/', UserForgotPasswordView.as_view(), name='password_reset'),
path('set-new-password/<uidb64>/<token>/', UserPasswordResetConfirmView.as_view(), name='password_reset_confirm'),
path('register/', UserRegisterView.as_view(), name='register'),
path('email-confirmation-sent/', EmailConfirmationSentView.as_view(), name='email_confirmation_sent'),
path('confirm-email/<str:uidb64>/<str:token>/', UserConfirmEmailView.as_view(), name='confirm_email'),
path('email-confirmed/', EmailConfirmedView.as_view(), name='email_confirmed'),
path('confirm-email-failed/', EmailConfirmationFailedView.as_view(), name='email_confirmation_failed'),
path('feedback/', FeedbackCreateView.as_view(), name='feedback'),
]
Добавим отображение обратной связи в админ-панеле
В файл admin.py нашего приложения system добавим следующий фрагмент кода:
from django.contrib import admin
from .models import Feedback
@admin.register(Feedback)
class FeedbackAdmin(admin.ModelAdmin):
"""
Админ-панель модели профиля
"""
list_display = ('email', 'ip_address', 'user')
list_display_links = ('email', 'ip_address')
Добавление шаблона для письма и формы
В папке templates/system/ создадим файл feedback.html, а внутри папки templates/system/email/ создадим файл шаблона письма feedback_email_send.html
Заполняем HTML файл feedback.html следующей разметкой:
{% extends 'main.html' %}
{% load static %}
{% block content %}
<div class="card mb-3 border-0 nth-shadow">
<div class="card-body">
<div class="card-title nth-card-title">
<h4>Контактная форма</h4>
</div>
<form method="post" action="{% url 'feedback' %}">
{% csrf_token %}
{{ form.as_p }}
<div class="d-grid gap-2 d-md-block mt-2">
<button type="submit" class="btn btn-dark">Отправить письмо</button>
</div>
</form>
</div>
</div>
{% endblock %}
А файл feedback_email_send.html следующей разметкой:
{% autoescape off %}
Здравствуйте, Администратор,
С контактной формы поступило новое сообщение со следующим содержимым:
{{ content }}
IP Адрес отправителя: {{ ip }}
Email отправителя: {{ email }}
{% if user %}Пользователь: {{ user.username }}{% endif %}
Команда сайта Django Base Project
{% endautoescape %}
Отлично. Перейдем к проверке.
Проверка работы обратной связи
Как авторизованный пользователь:
Перейдем на страницу: http://127.0.0.1:8000/feedback/
Как неавторизованный пользователь:
Отлично. Надеюсь у вас все получилось!