Hello'\ncleaned = bleach.clean(user_input)\nprint(cleaned) # Hello\n\n# SQL injection защита (используй параметризованные запросы)\n# Плохо:\nquery = f\"SELECT * FROM users WHERE email = '{email}'\"\n# Хорошо:\nquery = \"SELECT * FROM users WHERE email = %s\"\ncursor.execute(query, (email,))\n\n# HTML экранирование\nfrom html import escape\nuser_name = ''\nsafe_name = escape(user_name)\nprint(safe_name) # <img src=x onerror=alert(1)>\n\n# JSON валидация\nimport json\ntry:\n data = json.loads(user_input)\nexcept json.JSONDecodeError:\n raise ValueError('Невалидный JSON')\n```\n\n## Полный пример: валидация при создании пользователя\n\n```python\nfrom pydantic import BaseModel, EmailStr, Field, validator\nfrom sqlalchemy.orm import Session\nfrom models import User\n\nclass UserCreate(BaseModel):\n email: EmailStr\n name: str = Field(..., min_length=2, max_length=100)\n password: str = Field(..., min_length=8, max_length=128)\n age: int = Field(..., ge=18, le=120)\n \n @validator('name')\n def name_alphanumeric(cls, v):\n if not all(c.isalnum() or c.isspace() for c in v):\n raise ValueError('Имя содержит невалидные символы')\n return v\n\ndef create_user(user_data: UserCreate, db: Session) -> User:\n # 1. Pydantic уже провалидировал данные\n \n # 2. Проверяем уникальность в БД\n existing = db.query(User).filter(User.email == user_data.email).first()\n if existing:\n raise ValueError('Email уже зарегистрирован')\n \n # 3. Хешируем пароль перед сохранением\n from werkzeug.security import generate_password_hash\n hashed_password = generate_password_hash(user_data.password)\n \n # 4. Создаём объект в БД (constraints проверяют ещё раз)\n user = User(\n email=user_data.email,\n name=user_data.name,\n password=hashed_password,\n age=user_data.age\n )\n \n db.add(user)\n db.commit()\n db.refresh(user)\n \n return user\n```\n\n## Выводы\n\n**Уровни валидации (от клиента к БД):**\n1. **Frontend** — быстрая обратная связь\n2. **Backend** — обязательна, проверка типов и формата\n3. **Database** — последняя линия защиты (constraints)\n\n**Лучшие практики:**\n- Никогда не доверяй только клиентской валидации\n- Используй Pydantic для валидации в Python\n- Добавляй constraints на уровне БД\n- Санитизируй опасные данные\n- Всегда логируй попытки невалидных данных\n- Пароли хеш перед сохранением\n\n**Инструменты:**\n- **Pydantic** — валидация в Python\n- **email-validator** — строгая валидация email\n- **bleach** — санитизация HTML\n- **SQLAlchemy constraints** — валидация на уровне БД","dateCreated":"2026-03-22T20:50:05.614071","upvoteCount":0,"author":{"@type":"Person","name":"claude-haiku-4.5"}}}}
← Назад к вопросам

Какие знаешь виды проверок данных?

1.7 Middle🔥 171 комментариев
#Python Core

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

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

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

Виды проверок данных (Data Validation)

Проверка данных — критичный процесс для обеспечения качества, безопасности и корректности приложения. Проверки могут происходить на разных уровнях архитектуры.

1. Клиентская валидация (Frontend)

Проверяется в браузере ещё до отправки на сервер.

<!-- HTML5 встроенная валидация -->
<form>
    <!-- Обязательное поле -->
    <input type="text" name="name" required>
    
    <!-- Email валидация -->
    <input type="email" name="email" required>
    
    <!-- Числовое поле с диапазоном -->
    <input type="number" name="age" min="18" max="120" required>
    
    <!-- Паттерн -->
    <input type="text" name="phone" pattern="[0-9]{10}" placeholder="10 цифр">
    
    <!-- Минимальная длина -->
    <input type="password" name="password" minlength="8" required>
</form>

JavaScript валидация:

// Простая валидация
function validateEmail(email) {
    const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return regex.test(email);
}

// Более строгая валидация
const isValidEmail = (email) => {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) &&
           email.length <= 254;
};

// Валидация формы
function validateForm(formData) {
    const errors = {};
    
    if (!formData.name || formData.name.trim().length < 2) {
        errors.name = 'Имя должно быть минимум 2 символа';
    }
    
    if (!isValidEmail(formData.email)) {
        errors.email = 'Невалидный email';
    }
    
    if (formData.password.length < 8) {
        errors.password = 'Пароль минимум 8 символов';
    }
    
    return Object.keys(errors).length === 0 ? null : errors;
}

Недостаток: клиентскую валидацию легко обойти

2. Серверная валидация (Backend)

Обязательна! Клиентская валидация может быть отключена.

# Базовая валидация в Python
def validate_user_input(data):
    errors = {}
    
    if not data.get('email'):
        errors['email'] = 'Email обязателен'
    elif not '@' in data['email']:
        errors['email'] = 'Невалидный email'
    
    if not data.get('password'):
        errors['password'] = 'Пароль обязателен'
    elif len(data['password']) < 8:
        errors['password'] = 'Пароль минимум 8 символов'
    
    if data.get('age'):
        try:
            age = int(data['age'])
            if age < 18 or age > 120:
                errors['age'] = 'Возраст должен быть от 18 до 120'
        except ValueError:
            errors['age'] = 'Возраст должен быть числом'
    
    return errors if errors else None

3. Валидация типов данных (Type Validation)

Проверка что данные правильного типа.

# Встроенная проверка типов в Python
def process_user(name: str, age: int) -> dict:
    if not isinstance(name, str):
        raise TypeError(f'name должна быть str, получен {type(name)}')
    if not isinstance(age, int):
        raise TypeError(f'age должна быть int, получена {type(age)}')
    
    return {'name': name, 'age': age}

# Pydantic — лучший инструмент для валидации
from pydantic import BaseModel, EmailStr, Field

class User(BaseModel):
    name: str = Field(..., min_length=2, max_length=100)
    email: EmailStr  # Встроенная валидация email
    age: int = Field(..., ge=18, le=120)  # greater_equal, less_equal
    password: str = Field(..., min_length=8)
    
    class Config:
        strict = True

# Использование
try:
    user = User(
        name='John',
        email='john@example.com',
        age=25,
        password='securepass123'
    )
    print(user)  # Валидные данные
except Exception as e:
    print(f'Ошибка валидации: {e}')

# Невалидные данные
try:
    user = User(
        name='J',  # Слишком короткое
        email='invalid-email',  # Невалидный email
        age=15,  # Меньше 18
        password='short'  # Меньше 8 символов
    )
except Exception as e:
    print(f'Ошибка: {e}')

4. Валидация на уровне БД (Database Constraints)

Проверка непосредственно при сохранении в БД.

-- NOT NULL constraint
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) NOT NULL,
    name VARCHAR(100) NOT NULL
);

-- UNIQUE constraint
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    username VARCHAR(50) UNIQUE NOT NULL
);

-- CHECK constraint
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    age INT CHECK (age >= 18 AND age <= 120),
    salary DECIMAL CHECK (salary >= 0)
);

-- DEFAULT value
CREATE TABLE posts (
    id SERIAL PRIMARY KEY,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    status VARCHAR(20) DEFAULT 'draft'
);

-- Foreign Key constraint
CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

В SQLAlchemy:

from sqlalchemy import Column, Integer, String, CheckConstraint, UniqueConstraint
from sqlalchemy.orm import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    
    id = Column(Integer, primary_key=True)
    email = Column(String(255), unique=True, nullable=False)
    age = Column(Integer, CheckConstraint('age >= 18 AND age <= 120'))
    salary = Column(Integer, CheckConstraint('salary >= 0'))
    
    # Составной уникальный индекс
    __table_args__ = (
        UniqueConstraint('email', 'username', name='uq_email_username'),
    )

5. Валидация формата (Format Validation)

Проверка формата строк (email, URL, номер телефона и т.д.).

import re
from email_validator import validate_email, EmailNotValidError

# Email валидация
def is_valid_email(email):
    try:
        # Используй email_validator для правильной валидации
        validate_email(email, check_deliverability=False)
        return True
    except EmailNotValidError:
        return False

# URL валидация
url_pattern = r'^https?:\/\/.+\..+'
url = 'https://example.com'
if re.match(url_pattern, url):
    print('Валидный URL')

# Номер телефона
phone_pattern = r'^\+?1?\d{9,15}$'
phone = '+79999999999'
if re.match(phone_pattern, phone):
    print('Валидный номер')

# Credit card
card_pattern = r'^\d{13,19}$'  # Упрощённый паттерн

# UUID
import uuid
try:
    uuid.UUID('550e8400-e29b-41d4-a716-446655440000')
    print('Валидный UUID')
except ValueError:
    print('Невалидный UUID')

# Pydantic validators
from pydantic import BaseModel, validator

class Article(BaseModel):
    url: str
    slug: str
    
    @validator('url')
    def validate_url(cls, v):
        if not v.startswith(('http://', 'https://')):
            raise ValueError('URL должен начинаться с http:// или https://')
        return v
    
    @validator('slug')
    def validate_slug(cls, v):
        if not re.match(r'^[a-z0-9-]+$', v):
            raise ValueError('Slug содержит недопустимые символы')
        return v

6. Валидация диапазонов (Range Validation)

Проверка что значение находится в допустимом диапазоне.

# Числовой диапазон
def validate_age(age):
    if not isinstance(age, int):
        raise TypeError('Возраст должен быть числом')
    if age < 0 or age > 150:
        raise ValueError('Возраст должен быть от 0 до 150')
    return age

# Длина строки
def validate_password_length(password):
    if len(password) < 8:
        raise ValueError('Пароль минимум 8 символов')
    if len(password) > 128:
        raise ValueError('Пароль максимум 128 символов')
    return password

# Диапазон дат
from datetime import datetime, timedelta

def validate_date_range(date_str):
    date = datetime.fromisoformat(date_str)
    if date < datetime.now() - timedelta(days=365):
        raise ValueError('Дата слишком старая')
    if date > datetime.now() + timedelta(days=1):
        raise ValueError('Дата в будущем')
    return date

7. Кроссфилдовая валидация (Cross-field Validation)

Проверка связи между несколькими полями.

from pydantic import BaseModel, validator, root_validator

class UserRegistration(BaseModel):
    password: str
    password_confirm: str
    birth_date: str
    
    @root_validator
    def validate_passwords_match(cls, values):
        if values.get('password') != values.get('password_confirm'):
            raise ValueError('Пароли не совпадают')
        return values
    
    @validator('birth_date')
    def validate_age(cls, v, values):
        from datetime import datetime
        birth = datetime.fromisoformat(v)
        age = (datetime.now() - birth).days // 365
        if age < 18:
            raise ValueError('Вы должны быть старше 18 лет')
        return v

8. Санитизация данных (Data Sanitization)

Очистка данных от опасного контента.

# Удаление опасных символов
import bleach

user_input = '<script>alert("XSS")</script>Hello'
cleaned = bleach.clean(user_input)
print(cleaned)  # Hello

# SQL injection защита (используй параметризованные запросы)
# Плохо:
query = f"SELECT * FROM users WHERE email = '{email}'"
# Хорошо:
query = "SELECT * FROM users WHERE email = %s"
cursor.execute(query, (email,))

# HTML экранирование
from html import escape
user_name = '<img src=x onerror=alert(1)>'
safe_name = escape(user_name)
print(safe_name)  # &lt;img src=x onerror=alert(1)&gt;

# JSON валидация
import json
try:
    data = json.loads(user_input)
except json.JSONDecodeError:
    raise ValueError('Невалидный JSON')

Полный пример: валидация при создании пользователя

from pydantic import BaseModel, EmailStr, Field, validator
from sqlalchemy.orm import Session
from models import User

class UserCreate(BaseModel):
    email: EmailStr
    name: str = Field(..., min_length=2, max_length=100)
    password: str = Field(..., min_length=8, max_length=128)
    age: int = Field(..., ge=18, le=120)
    
    @validator('name')
    def name_alphanumeric(cls, v):
        if not all(c.isalnum() or c.isspace() for c in v):
            raise ValueError('Имя содержит невалидные символы')
        return v

def create_user(user_data: UserCreate, db: Session) -> User:
    # 1. Pydantic уже провалидировал данные
    
    # 2. Проверяем уникальность в БД
    existing = db.query(User).filter(User.email == user_data.email).first()
    if existing:
        raise ValueError('Email уже зарегистрирован')
    
    # 3. Хешируем пароль перед сохранением
    from werkzeug.security import generate_password_hash
    hashed_password = generate_password_hash(user_data.password)
    
    # 4. Создаём объект в БД (constraints проверяют ещё раз)
    user = User(
        email=user_data.email,
        name=user_data.name,
        password=hashed_password,
        age=user_data.age
    )
    
    db.add(user)
    db.commit()
    db.refresh(user)
    
    return user

Выводы

Уровни валидации (от клиента к БД):

  1. Frontend — быстрая обратная связь
  2. Backend — обязательна, проверка типов и формата
  3. Database — последняя линия защиты (constraints)

Лучшие практики:

  • Никогда не доверяй только клиентской валидации
  • Используй Pydantic для валидации в Python
  • Добавляй constraints на уровне БД
  • Санитизируй опасные данные
  • Всегда логируй попытки невалидных данных
  • Пароли хеш перед сохранением

Инструменты:

  • Pydantic — валидация в Python
  • email-validator — строгая валидация email
  • bleach — санитизация HTML
  • SQLAlchemy constraints — валидация на уровне БД