Уявіть, що ви охоронець на вході в клуб. Кожного разу, коли хтось намагається зайти, ви перевіряєте його документи, щоб переконатися, що все в порядку. Аналогічно, валідація допомагає програмі переконатися, що дані, введені користувачем, відповідають очікуваним вимогам.
Django надає нам як вбудовані інструменти для валідації, так і можливість створювати свої власні валідатори. У цій лекції ми розберемо обидва підходи.
Вбудовані валідатори Django
Django "з коробки" надає багато зручних і потужних валідаторів, які полегшують життя розробнику. Ці валідатори можна використовувати прямо в моделях або формах.
Приклади вбудованих валідаторів
Перевірка унікальності даних (наприклад,
usernameіemail): Django автоматично перевіряє унікальність полів, якщо ви вказали це в моделі.Перевірка на мінімальну та максимальну довжину: такі валідатори використовуються для перевірки довжини тексту, введеного користувачем.
Перевірка формату email: Django надає валідатор для перевірки коректності формату email.
Валідація паролю: вбудовані валідатори паролю допомагають переконатися, що придуманий користувачем пароль достатньо складний.
Приклад використання вбудованих валідаторів
Давайте додамо додаткову перевірку унікальності username і коректності формату email у форму реєстрації:
from django import forms
from django.contrib.auth.models import User
class RegistrationForm(forms.ModelForm):
class Meta:
model = User
fields = ['username', 'email', 'password']
def clean_username(self):
username = self.cleaned_data.get('username')
if User.objects.filter(username=username).exists():
raise forms.ValidationError("Таке ім'я користувача вже зайняте. Будь ласка, оберіть інше.")
return username
def clean_email(self):
email = self.cleaned_data.get('email')
if User.objects.filter(email=email).exists():
raise forms.ValidationError("Email вже використовується. Будь ласка, вкажіть інший.")
return email
- Метод
clean_<field>викликається для кожного поля. Тут ми перевіряємо унікальністьusernameіemail. - Якщо дані некоректні, викликається
forms.ValidationError, який автоматично передає повідомлення про помилку в шаблон.
Кастомні валідатори
Але що робити, якщо вбудовані валідатори не покривають усіх наших потреб? Наприклад, якщо ми хочемо, щоб ім'я користувача починалося з літери та містило тільки літери і цифри. Для таких випадків Django дозволяє створювати кастомні валідатори.
Приклад кастомного валідатора
Ми створимо валідатор, який перевіряє, щоб ім'я користувача починалося з літери.
import re
from django.core.exceptions import ValidationError
def validate_username(value):
if not re.match("^[A-Za-z][A-Za-z0-9]*$", value):
raise ValidationError(
"Ім'я користувача має починатися з літери та містити тільки літери і цифри."
)
Тепер ми можемо підключити цей валідатор у нашій формі реєстрації:
class RegistrationForm(forms.ModelForm):
username = forms.CharField(validators=[validate_username])
class Meta:
model = User
fields = ['username', 'email', 'password']
І, вуаля, наш користувач більше не зможе реєструватися як "123abc".
Запобігання вразливостям
Дані від користувача завжди мають бути під підозрою, особливо якщо вони надходять через веб-форми. Валідація — це перший рубіж оборони. Без неї ваш сайт може стати жертвою:
- SQL-ін'єкцій: зловмисники можуть намагатися передати SQL-код замість даних.
- XSS-атак: вставка скриптів для виконання на стороні клієнта.
- Дублювання даних: повторювані записи в базі даних.
Коли ви пишете валідацію, подумайте, що може піти не так, і закрийте всі дірки.
Обробка помилок валідації
Помилки валідації — це не катастрофа, а просто сигнал користувачу, що він зробив щось не так. У Django це обробляється через об'єкт form.errors.
Приклад відображення помилок у шаблоні:
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
{% if form.errors %}
<ul>
{% for field, errors in form.errors.items %}
<li>{{ field }}: {{ errors|join:", " }}</li>
{% endfor %}
</ul>
{% endif %}
<button type="submit">Реєстрація</button>
</form>
Тепер, якщо у користувача щось пішло не так, він побачить ввічливе повідомлення про помилку.
Перевірка паролів
Паролі — це окрема пісня. Django надає PasswordValidator для перевірки складності пароля. Наприклад, ви можете додати перевірку довжини пароля або його схожості з ім’ям користувача.
Приклад налаштування валідаторів пароля у settings.py:
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {
'min_length': 8,
}
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
Таким чином, користувачі не зможуть використовувати "12345678" або "password" як свої паролі.
Практичне завдання
Припустимо, нам потрібно реалізувати наступну логіку валідації:
- Ім'я користувача має бути унікальним і починатися з літери.
- Email не має вже існувати в базі даних.
- Пароль має бути не менш ніж 8 символів, містити принаймні одну цифру та одну літеру.
Спробуємо реалізувати це:
from django import forms
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
import re
def validate_password_strength(password):
if len(password) < 8:
raise ValidationError("Пароль має містити не менш ніж 8 символів.")
if not re.search(r"\d", password):
raise ValidationError("Пароль має містити принаймні одну цифру.")
if not re.search(r"[A-Za-z]", password):
raise ValidationError("Пароль має містити принаймні одну літеру.")
class RegistrationForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput, validators=[validate_password_strength])
class Meta:
model = User
fields = ['username', 'email', 'password']
def clean_username(self):
username = self.cleaned_data.get('username')
if not re.match("^[A-Za-z][A-Za-z0-9]*$", username):
raise forms.ValidationError("Ім'я користувача має починатися з літери та містити тільки літери і цифри.")
if User.objects.filter(username=username).exists():
raise forms.ValidationError("Таке ім'я користувача вже зайняте.")
return username
def clean_email(self):
email = self.cleaned_data.get('email')
if User.objects.filter(email=email).exists():
raise forms.ValidationError("Цей email вже використовується.")
return email
Тепер наші форми готові до будь-якої атаки, ну або майже будь-якої!
Типові помилки при валідації
- Забуття про використання методу
cleaned_data. У фінальній формі обробляються лише дані, що пройшли валідацію. - Відсутність перевірки унікальності на рівні моделі. Навіть якщо валідатор пропустить дані, база даних не повинна.
- Занадто загальні повідомлення про помилки. Користувачі не повинні здогадуватись, що вони зробили не так.
Тепер, коли ви знаєте, як працювати з валідацією, ваші проєкти зможуть справлятися з будь-якою користувацькою "креативністю". Не забувайте тестувати свої валідатори та регулярно оновлювати їх залежно від вимог застосунку.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ