JavaRush /Курси /Модуль 3: Django /Валідація даних при реєстрації

Валідація даних при реєстрації

Модуль 3: Django
Рівень 15 , Лекція 5
Відкрита

Уявіть, що ви охоронець на вході в клуб. Кожного разу, коли хтось намагається зайти, ви перевіряєте його документи, щоб переконатися, що все в порядку. Аналогічно, валідація допомагає програмі переконатися, що дані, введені користувачем, відповідають очікуваним вимогам.

Django надає нам як вбудовані інструменти для валідації, так і можливість створювати свої власні валідатори. У цій лекції ми розберемо обидва підходи.

Вбудовані валідатори Django

Django "з коробки" надає багато зручних і потужних валідаторів, які полегшують життя розробнику. Ці валідатори можна використовувати прямо в моделях або формах.

Приклади вбудованих валідаторів

  1. Перевірка унікальності даних (наприклад, username і email): Django автоматично перевіряє унікальність полів, якщо ви вказали це в моделі.

  2. Перевірка на мінімальну та максимальну довжину: такі валідатори використовуються для перевірки довжини тексту, введеного користувачем.

  3. Перевірка формату email: Django надає валідатор для перевірки коректності формату email.

  4. Валідація паролю: вбудовані валідатори паролю допомагають переконатися, що придуманий користувачем пароль достатньо складний.

Приклад використання вбудованих валідаторів

Давайте додамо додаткову перевірку унікальності 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" як свої паролі.

Практичне завдання

Припустимо, нам потрібно реалізувати наступну логіку валідації:

  1. Ім'я користувача має бути унікальним і починатися з літери.
  2. Email не має вже існувати в базі даних.
  3. Пароль має бути не менш ніж 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. У фінальній формі обробляються лише дані, що пройшли валідацію.
  • Відсутність перевірки унікальності на рівні моделі. Навіть якщо валідатор пропустить дані, база даних не повинна.
  • Занадто загальні повідомлення про помилки. Користувачі не повинні здогадуватись, що вони зробили не так.

Тепер, коли ви знаєте, як працювати з валідацією, ваші проєкти зможуть справлятися з будь-якою користувацькою "креативністю". Не забувайте тестувати свої валідатори та регулярно оновлювати їх залежно від вимог застосунку.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ