Создание сайта на Django: Урок 34, добавление обратной связи в Django
avatar
7 | (offline)
❤️‍🔥Notehunter Developer
Добавлено:
Категория: Руководства «Django»
Комментариев: 0

Создание модели обратной связи:

Создадим нашу модель в модуле "system". Поэтому создаем файл feedback.py в папке models.

modules/system/models/feedback.py

from django.contrib.auth.models import User
from django.db import models


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='Содержимое')
    created_at = 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 = ('-created_at',)
        db_table = 'app_feedback'

    def __str__(self):
        return f'Вам письмо: {self.email}'

Пояснения:

  • В этой моделе мы будем получать ip, email, пользователя (если есть)

Напоминаю, если вы используете структуру папок как и я, то добавим модель в __init__.py

modules/system/models/__init__.py

from modules.system.models.abstract import AbstractBaseMeta
from modules.system.models.feedback import Feedback
from modules.system.models.profiles import Profile

__all__ = (
    'AbstractBaseMeta',
    'Profile',
    'Feedback',
)

Обязательно делаем миграции:

(venv) PS C:\Users\Razilator\Desktop\Courses\App\backend> python manage.py makemigrations
Migrations for 'system':
  modules\system\migrations\0002_feedback.py
    - Create model Feedback
(venv) PS C:\Users\Razilator\Desktop\Courses\App\backend> python manage.py migrate       
Operations to perform:
  Apply all migrations: admin, auth, blog, contenttypes, sessions, sites, system
Running migrations:
  Applying system.0002_feedback... OK

Далее создадим форму для обратной связи:

modules/system/forms/feedback.py

from captcha.fields import ReCaptchaField
from captcha.widgets import ReCaptchaV2Checkbox
from django import forms

from backend import settings
from modules.system.models import Feedback


class CreateFeedbackForm(forms.ModelForm):
    """
    Форма отправки обратной связи
    """
    class Meta:
        model = Feedback
        fields = ('subject', 'email', 'content')

    recaptcha = ReCaptchaField(widget=ReCaptchaV2Checkbox, public_key=settings.RECAPTCHA_PUBLIC_KEY, private_key=settings.RECAPTCHA_PRIVATE_KEY, label='Капча')

    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'
            })

Пояснения:

  • В форму выведем только тему, емейл и текст сообщения.
  • Добавим ReCaptcha из прошлого урока.

Добавим в __init__.py

modules/system/forms/__init__.py

from modules.system.forms.authenticated import *
from modules.system.forms.profiles import *
from modules.system.forms.feedback import *

__all__ = '__all__'

Теперь создадим представление для обратной связи:

modules/system/views/feedback.py

from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy
from django.views.generic import CreateView

from modules.system.forms.feedback import CreateFeedbackForm
from modules.system.models import Feedback
from modules.system.services.email import send_contact_email_message
from modules.system.services.utils import get_client_ip


class FeedbackCreateView(SuccessMessageMixin, CreateView):
    model = Feedback
    form_class = CreateFeedbackForm
    success_message = 'Ваше письмо успешно отправлено администрации сайта'
    template_name = 'modules/system/feedback/feedback.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Контактная форма'
        return context

    def form_valid(self, form):
        current_user = self.request.user
        if form.is_valid():
            feedback = form.save(commit=False)
            feedback.ip_address = get_client_ip(self.request)
            if current_user.is_authenticated:
                feedback.user = current_user
            user_id = getattr(feedback, 'user_id', None)
            send_contact_email_message(feedback.subject, feedback.email, feedback.content, feedback.ip_address, user_id)
        return super().form_valid(form)

    def get_success_url(self):
        return reverse_lazy('home')

Пояснения:

  • Я создаю обратную связь наследуясь от CreateView.
  • В конце мы получаем ip адрес с помощью функции, а также отправляем сообщение с помощью функции, которую мы создадим далее.

Не забываем про __init__.py который я использую для декомпозиции:

modules/system/views/__init__.py

from modules.system.views.profiles import *
from modules.system.views.authenticated import *
from modules.system.views.feedback import *

__all__ = '__all__'

Функция получения IP адреса:

Файл utils.py мы уже создавали в папке services в одном из уроков, поэтому идем в этот файлик и добавляем туда нашу функцию:

modules/system/services/utils.py

def get_client_ip(request):
    """
    Получение IP юзера
    """
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

А теперь добавим функцию отправки сообщения, также в одном из уроков мы создали файл email.py в папке services:

modules/system/services/email.py

from django.core.mail import EmailMessage
from backend import settings

def send_contact_email_message(subject, email, content, ip, user_id):
    """
    Функция отправки контактной формы
    """
    if user_id:
        user = User.objects.get(id=user_id)
    else:
        user = None
    message = render_to_string('modules/system/feedback/email/contact_mail.html', {
        'email': email,
        'content': content,
        'ip': ip,
        'user': user,
    })
    email = EmailMessage(subject, message, settings.EMAIL_SERVER, settings.EMAIL_ADMIN)
    return email.send(fail_silently=False)

Пояснения:

  • Если юзер есть, получаем его по id, передаем в контекст email сообщения.
  • Используем SMTP из настроек.

Теперь осталось добавить шаблоны и urls.py

В templates/modules/system я создаю папку feedback, а в ней папку email и рядом с папкой email файл feedback.html

templates/modules/system/feedback/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-create-view' %}">
                {% 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 %}

Также добавляем шаблон письма:

templates/modules/system/feedback/email/contact_mail.html

{% autoescape off %}

Здравствуйте, Администратор,

С контактной формы поступило новое сообщение со следующим содержимым:
 
    {{ content }}

    IP Адрес отправителя: {{ ip }}
    Email отправителя: {{ email }}
    {% if user %}Пользователь: {{ user.username }}{% endif %}

Команда сайта Django Project

{% endautoescape %}

И наконец добавим ссылку на контактную форму:

modules/system/urls.py

from django.urls import path

from modules.system.views import ProfileView, ProfileEditView, RegisterCreateView, UserLoginView, \
    UserPasswordChangeView, UserForgotPasswordView, UserPasswordResetConfirmView, UserLogoutView, ActivateAccountView, \
    FeedbackCreateView

urlpatterns = [
    path('user/edit/', ProfileEditView.as_view(), name='profile-edit'),
    path('user/<str:slug>/', ProfileView.as_view(), name='profile'),
    path('register/', RegisterCreateView.as_view(), name='register'),
    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('activate/<uidb64>/<token>/', ActivateAccountView.as_view(), name='activate'),
    path('feedback/', FeedbackCreateView.as_view(), name='feedback-create-view'),
]

Давайте проверим нашу форму на сайте:

И само письмо:

Отлично, мы создали форму обратной связи с моделью, защитили от спама. 

Комментарии к статье 0
Комментариев нет
Форма добавления комментария (необходима регистрация)