Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Валидация (Validation)
Валидация — это процесс проверки корректности и полноты данных перед их обработкой в системе. Это критический процесс, который гарантирует, что приложение работает только с правильными данными, соответствующими определённым правилам и требованиям.
Основные концепции
Валидация проверяет:
- Тип данных — число, строка, объект
- Формат — email, телефон, URL, дата
- Диапазон — число в пределах min-max
- Длина — строка не короче/длиннее заданного
- Наличие — обязательные поля заполнены
- Уникальность — значение не повторяется в БД
- Консистентность — логическая связь между полями
- Безопасность — отсутствие 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}" # ОПАСНО!
Вывод: Валидация — это не опциональный процесс, а обязательная часть безопасной разработки, которая защищает приложение от ошибок, атак и неправильных данных.