← Назад к вопросам

Что такое валидация?

1.8 Middle🔥 211 комментариев
#DevOps и инфраструктура#Django

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Валидация (Validation)

Валидация — это процесс проверки корректности и полноты данных перед их обработкой в системе. Это критический процесс, который гарантирует, что приложение работает только с правильными данными, соответствующими определённым правилам и требованиям.

Основные концепции

Валидация проверяет:

  1. Тип данных — число, строка, объект
  2. Формат — email, телефон, URL, дата
  3. Диапазон — число в пределах min-max
  4. Длина — строка не короче/длиннее заданного
  5. Наличие — обязательные поля заполнены
  6. Уникальность — значение не повторяется в БД
  7. Консистентность — логическая связь между полями
  8. Безопасность — отсутствие SQL injection, XSS и т.п.

Где валидировать?

┌─────────────────────────────────────────────┐
│ 1. Frontend (UI) — быстрая обратная связь │
│ 2. API (Backend) — главная защита         │
│ 3. База данных — последний рубеж защиты   │
└─────────────────────────────────────────────┘

Валидация в Python: Pydantic

from pydantic import BaseModel, Field, EmailStr, field_validator
from typing import Optional

# Определение схемы данных
class User(BaseModel):
    id: int
    name: str = Field(..., min_length=1, max_length=100)
    email: EmailStr  # Автоматическая валидация email
    age: int = Field(..., ge=0, le=150)  # От 0 до 150
    phone: Optional[str] = None
    
    @field_validator('name')
    @classmethod
    def name_must_be_alphabetic(cls, v):
        if not v.isalpha():
            raise ValueError('Имя должно содержать только буквы')
        return v

# Использование
try:
    user = User(
        id=1,
        name="John",
        email="john@example.com",
        age=30
    )
    print(user.model_dump())  # Успех
except ValueError as e:
    print(f"Ошибка валидации: {e}")

# Ошибка валидации
try:
    invalid_user = User(
        id=1,
        name="John123",  # Ошибка: содержит цифры
        email="invalid-email",  # Ошибка: неправильный формат
        age=200  # Ошибка: возраст > 150
    )
except ValueError as e:
    print(f"Ошибки валидации: {e}")

Типы валидации

1. Валидация типа

from pydantic import BaseModel

class Product(BaseModel):
    name: str  # Должна быть строка
    price: float  # Должно быть число
    count: int  # Целое число
    active: bool  # Логическое значение

# Автоматическое преобразование типов
product = Product(
    name="Laptop",
    price="99.99",  # Строка → float
    count="5",  # Строка → int
    active=1  # 1 → True
)
print(product.price)  # 99.99

2. Валидация диапазона

from pydantic import BaseModel, Field

class Article(BaseModel):
    title: str = Field(..., min_length=5, max_length=200)
    content: str = Field(..., min_length=100)
    rating: int = Field(..., ge=1, le=5)  # От 1 до 5
    views: int = Field(default=0, ge=0)  # Не менее 0

3. Валидация формата

from pydantic import BaseModel, EmailStr, HttpUrl, UUID4
from datetime import datetime

class Contact(BaseModel):
    email: EmailStr  # Email
    website: HttpUrl  # HTTP URL
    user_id: UUID4  # UUID
    joined: datetime  # ISO 8601 дата-время
    phone: str = Field(..., regex=r'^\+?\d{10,15}$')  # Регулярное выражение

4. Пользовательская валидация

from pydantic import BaseModel, field_validator, model_validator

class User(BaseModel):
    password: str
    password_confirm: str
    
    @field_validator('password')
    @classmethod
    def password_strong(cls, v):
        if len(v) < 8:
            raise ValueError('Пароль должен быть минимум 8 символов')
        if not any(c.isupper() for c in v):
            raise ValueError('Пароль должен содержать заглавную букву')
        return v
    
    # Валидация между полями
    @model_validator(mode='after')
    def passwords_match(self):
        if self.password != self.password_confirm:
            raise ValueError('Пароли не совпадают')
        return self

5. Валидация БД (уникальность)

from pydantic import field_validator, BaseModel
from sqlalchemy import select
from sqlalchemy.orm import Session

class RegisterRequest(BaseModel):
    email: str
    username: str
    
    @field_validator('email')
    @classmethod
    async def email_exists(cls, v, info):
        # Проверить в БД
        db = info.context.get('db')  # Передать БД в контекст
        existing = db.query(User).filter(User.email == v).first()
        if existing:
            raise ValueError('Email уже зарегистрирован')
        return v

Валидация в FastAPI

from fastapi import FastAPI, Query, Path, Body
from pydantic import BaseModel, Field

app = FastAPI()

class Item(BaseModel):
    name: str = Field(..., min_length=1, max_length=100)
    price: float = Field(..., gt=0)  # Больше 0
    description: str = None
    tax: float = None

@app.post("/items/")
async def create_item(item: Item):
    # item автоматически валидируется
    return item

@app.get("/items/")
async def get_items(
    skip: int = Query(0, ge=0),  # Минимум 0
    limit: int = Query(10, ge=1, le=100),  # От 1 до 100
):
    return {"skip": skip, "limit": limit}

@app.get("/items/{item_id}")
async def get_item(item_id: int = Path(..., gt=0)):
    return {"item_id": item_id}

Валидация в WTForms (для форм)

from wtforms import Form, StringField, PasswordField, validators
from wtforms.validators import Email, Length, EqualTo

class RegistrationForm(Form):
    username = StringField(
        'Username',
        [validators.Length(min=4, max=25)]
    )
    email = StringField(
        'Email',
        [validators.Email()]
    )
    password = PasswordField(
        'Password',
        [validators.Length(min=8)]
    )
    confirm = PasswordField(
        'Confirm Password',
        [validators.EqualTo('password', message='Пароли не совпадают')]
    )

Многоуровневая валидация

from pydantic import BaseModel, field_validator, model_validator

class Order(BaseModel):
    user_id: int
    items: list[dict]
    total_price: float
    
    @field_validator('user_id')
    @classmethod
    def user_exists(cls, v):
        # Проверить в БД
        if not db.query(User).filter(User.id == v).exists():
            raise ValueError('Пользователь не найден')
        return v
    
    @field_validator('items')
    @classmethod
    def items_not_empty(cls, v):
        if not v:
            raise ValueError('Заказ должен содержать хотя бы один товар')
        return v
    
    @model_validator(mode='after')
    def validate_price(self):
        # Проверить соответствие цены
        calculated = sum(item['price'] * item['qty'] for item in self.items)
        if abs(calculated - self.total_price) > 0.01:
            raise ValueError('Сумма не соответствует товарам')
        return self

Практические советы

# ✅ Всегда валидируй на backend
# ❌ Не полагайся только на frontend валидацию

# ✅ Валидируй ВСЕ входные данные
# ❌ Не предполагай, что данные безопасны

# ✅ Дай понятные сообщения об ошибках
error = {
    "field": "email",
    "message": "Email должен быть действительным адресом"
}

# ❌ Не выводи внутренние детали системы в сообщениях об ошибках

Безопасность

# ✅ Защита от SQL injection
from sqlalchemy import text

# Валидировать и использовать параметризованные запросы
result = db.execute(
    text("SELECT * FROM users WHERE id = :id"),
    {"id": validated_id}
)

# ❌ Не использовать конкатенацию строк
# query = f"SELECT * FROM users WHERE id = {user_input}"  # ОПАСНО!

Вывод: Валидация — это не опциональный процесс, а обязательная часть безопасной разработки, которая защищает приложение от ошибок, атак и неправильных данных.