feat: add FAQ question form with email notifications and validations
All checks were successful
Build and Push Docker Image / build (push) Successful in 43s

- Created `FaqQuestionForm` in `forms.py` with custom field validations
- Added FAQ form to the FAQ section with CSRF protection and error handling
- Integrated email notifications to administrator upon FAQ submission
- Updated styles and scripts for form inputs, validation messages, and messages UI
- Adjusted FAQ section layout in `faq.html` and related CSS for responsiveness
- Implemented tests for FAQ form submission and email notifications
This commit is contained in:
2026-04-19 22:51:56 +02:00
parent 80164b833b
commit 4f4a1a55e5
13 changed files with 379 additions and 32 deletions

32
home/forms.py Normal file
View File

@@ -0,0 +1,32 @@
from django import forms
class FaqQuestionForm(forms.Form):
name = forms.CharField(
label="Имя",
max_length=150,
widget=forms.TextInput(attrs={"placeholder": "Имя", "autocomplete": "name"}),
)
phone = forms.CharField(
label="Номер телефона",
max_length=64,
widget=forms.TextInput(attrs={"placeholder": "Номер телефона", "autocomplete": "tel"}),
)
question = forms.CharField(
label="Ваш вопрос",
max_length=1000,
widget=forms.Textarea(attrs={"placeholder": "Ваш вопрос", "rows": 4, "autocomplete": "off"}),
)
accept = forms.BooleanField(
label="Я согласен с политикой обработки персональных данных",
error_messages={"required": "Необходимо согласие на обработку персональных данных."},
)
def clean_name(self):
return self.cleaned_data["name"].strip()
def clean_phone(self):
return self.cleaned_data["phone"].strip()
def clean_question(self):
return self.cleaned_data["question"].strip()

View File

@@ -1,3 +1,38 @@
from django.test import TestCase
from django.core import mail
from django.test import TestCase, override_settings
from django.urls import reverse
# Create your tests here.
@override_settings(EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend", ADMINISTRATOR_EMAIL="admin@example.com")
class FaqQuestionTests(TestCase):
def test_faq_question_form_sends_email_to_administrator(self):
response = self.client.post(
reverse("home"),
data={
"name": "Иван Петров",
"phone": "+375291112233",
"question": "Сколько стоит доставка?",
"accept": "on",
},
)
self.assertEqual(response.status_code, 302)
self.assertEqual(response["Location"], reverse("home") + "#faq-section")
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].to, ["admin@example.com"])
self.assertIn("Иван Петров", mail.outbox[0].body)
self.assertIn("Сколько стоит доставка?", mail.outbox[0].body)
def test_faq_question_form_requires_consent(self):
response = self.client.post(
reverse("home"),
data={
"name": "Иван Петров",
"phone": "+375291112233",
"question": "Сколько стоит доставка?",
},
)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(mail.outbox), 0)
self.assertContains(response, "Необходимо согласие на обработку персональных данных.")

View File

@@ -1,8 +1,48 @@
from django.shortcuts import render
import logging
from django.conf import settings
from django.core.mail import send_mail
from django.shortcuts import redirect, render
from django.urls import reverse
from django.contrib import messages
from home.forms import FaqQuestionForm
logger = logging.getLogger(__name__)
def index(request):
return render(request=request, template_name='home/index.html', context={})
form = FaqQuestionForm(request.POST or None)
if request.method == "POST" and form.is_valid():
subject = f"Новый вопрос с сайта {settings.SITE_NAME}"
message = "\n".join(
[
f"Имя: {form.cleaned_data['name']}",
f"Телефон: {form.cleaned_data['phone']}",
f"Вопрос: {form.cleaned_data['question']}",
"",
f"Страница: {request.build_absolute_uri('/')}",
]
)
try:
send_mail(
subject=subject,
message=message,
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[settings.ADMINISTRATOR_EMAIL],
fail_silently=False,
)
except Exception:
logger.exception("Failed to send FAQ question email")
form.add_error(None, "Не удалось отправить вопрос. Попробуйте позже.")
else:
messages.success(request, "Спасибо. Ваш вопрос отправлен, мы свяжемся с вами в ближайшее время.")
return redirect(f"{reverse('home')}#faq-section")
return render(request=request, template_name='home/index.html', context={"faq_form": form})
def services(request):
@@ -18,4 +58,4 @@ def warranty(request):
def contacts(request):
return render(request=request, template_name='home/contacts.html', context={})
return render(request=request, template_name='home/contacts.html', context={})