Skip to content

Instantly share code, notes, and snippets.

@Miron-Anosov
Created January 21, 2025 21:22
Show Gist options
  • Save Miron-Anosov/2ff7dad78998187cae4f22567c8b68ab to your computer and use it in GitHub Desktop.
Save Miron-Anosov/2ff7dad78998187cae4f22567c8b68ab to your computer and use it in GitHub Desktop.

Django Forms - Полное руководство

Содержание

Введение в Django Forms

Django Forms предоставляет мощный инструмент для:

  • Генерации HTML-форм
  • Валидации данных
  • Преобразования данных в Python-типы
  • Защиты от распространенных уязвимостей

Типы форм

1. Form

Базовый класс для создания форм:

from django import forms

class SimpleForm(forms.Form):
    username = forms.CharField(max_length=100)
    email = forms.EmailField()
    message = forms.CharField(widget=forms.Textarea)

2. ModelForm

Форма, связанная с моделью:

from django import forms
from .models import User

class UserForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ['username', 'email', 'bio']
        # или fields = '__all__' для всех полей
        exclude = ['created_at']  # исключение полей

Поля форм

Основные типы полей

class CompleteForm(forms.Form):
    # Текстовые поля
    text = forms.CharField(max_length=100)
    email = forms.EmailField()
    url = forms.URLField()
    
    # Числовые поля
    number = forms.IntegerField(min_value=0, max_value=100)
    decimal = forms.DecimalField(max_digits=5, decimal_places=2)
    
    # Поля выбора
    choice = forms.ChoiceField(choices=[('1', 'One'), ('2', 'Two')])
    multiple = forms.MultipleChoiceField(choices=[('1', 'One'), ('2', 'Two')])
    
    # Булево поле
    boolean = forms.BooleanField(required=False)
    
    # Поля даты и времени
    date = forms.DateField()
    time = forms.TimeField()
    datetime = forms.DateTimeField()

Параметры полей

class FieldParametersExample(forms.Form):
    username = forms.CharField(
        label='Имя пользователя',  # Label для поля
        help_text='Введите ваше имя',  # Вспомогательный текст
        initial='John Doe',  # Значение по умолчанию
        required=True,  # Обязательное поле
        disabled=False,  # Отключено ли поле
        validators=[custom_validator]  # Пользовательские валидаторы
    )

Валидация

1. Встроенная валидация

class ValidationForm(forms.Form):
    username = forms.CharField(
        min_length=3,
        max_length=32,
        required=True
    )
    age = forms.IntegerField(
        min_value=18,
        max_value=99
    )

2. Пользовательские валидаторы

from django.core.exceptions import ValidationError

def validate_even(value):
    if value % 2 != 0:
        raise ValidationError('%(value)s не является четным числом', 
            params={'value': value})

class CustomValidationForm(forms.Form):
    even_number = forms.IntegerField(validators=[validate_even])

3. Валидация на уровне формы

class PasswordForm(forms.Form):
    password = forms.CharField(widget=forms.PasswordInput)
    password_confirm = forms.CharField(widget=forms.PasswordInput)
    
    def clean(self):
        cleaned_data = super().clean()
        password = cleaned_data.get('password')
        password_confirm = cleaned_data.get('password_confirm')
        
        if password and password_confirm and password != password_confirm:
            raise ValidationError('Пароли не совпадают')
        
        return cleaned_data

Виджеты

Основные виджеты

class WidgetsExample(forms.Form):
    # Текстовые виджеты
    text = forms.CharField(widget=forms.TextInput(attrs={
        'class': 'form-control',
        'placeholder': 'Введите текст'
    }))
    
    password = forms.CharField(widget=forms.PasswordInput)
    
    large_text = forms.CharField(widget=forms.Textarea)
    
    # Виджеты выбора
    select = forms.ChoiceField(widget=forms.Select)
    radio = forms.ChoiceField(widget=forms.RadioSelect)
    checkbox = forms.BooleanField(widget=forms.CheckboxInput)
    
    # Виджеты для дат
    date = forms.DateField(widget=forms.DateInput(attrs={
        'type': 'date'
    }))

Работа с файлами

Форма для загрузки файлов

from django.core.exceptions import ValidationError

ALLOWED_TYPES = ['image/jpeg', 'image/png']
MAX_FILE_SIZE = 5 * 1024 * 1024  # 5MB

def validate_file(file):
    if file.content_type not in ALLOWED_TYPES:
        raise ValidationError(f'Неподдерживаемый тип файла: {file.content_type}')
    
    if file.size > MAX_FILE_SIZE:
        raise ValidationError('Файл слишком большой')

class FileUploadForm(forms.Form):
    file = forms.FileField(
        validators=[validate_file],
        widget=forms.FileInput(attrs={
            'class': 'form-control',
            'accept': '.jpg,.jpeg,.png'
        })
    )

Обработка файлов в views

def upload_file(request):
    if request.method == 'POST':
        form = FileUploadForm(request.POST, request.FILES)
        if form.is_valid():
            handle_uploaded_file(request.FILES['file'])
            return redirect('success')
    else:
        form = FileUploadForm()
    return render(request, 'upload.html', {'form': form})

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

1. Структурирование форм

# forms/base.py
class BaseForm(forms.Form):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs.update({'class': 'form-control'})

# forms/user.py
class UserForm(BaseForm):
    username = forms.CharField(max_length=100)
    email = forms.EmailField()

2. Повторное использование валидаторов

# validators.py
from django.core.validators import RegexValidator

phone_validator = RegexValidator(
    regex=r'^\+?1?\d{9,15}$',
    message='Номер телефона должен быть в формате: "+999999999"'
)

# forms.py
class ContactForm(forms.Form):
    phone = forms.CharField(validators=[phone_validator])

3. Кастомные виджеты

class DatePickerWidget(forms.DateInput):
    def __init__(self, attrs=None):
        default_attrs = {'type': 'date'}
        if attrs:
            default_attrs.update(attrs)
        super().__init__(default_attrs)

class EventForm(forms.Form):
    date = forms.DateField(widget=DatePickerWidget)

4. Обработка ошибок

class FormWithCustomErrors(forms.Form):
    def add_error_class(self):
        for field in self:
            if field.errors:
                field.field.widget.attrs['class'] += ' is-invalid'

    def is_valid(self):
        valid = super().is_valid()
        if not valid:
            self.add_error_class()
        return valid

Заключение

Django Forms предоставляет мощный и гибкий инструментарий для работы с формами. Ключевые моменты:

  • Используйте ModelForm для форм, связанных с моделями
  • Применяйте пользовательскую валидацию где необходимо
  • Создавайте переиспользуемые компоненты
  • Следите за безопасностью при обработке файлов
  • Придерживайтесь DRY (Don't Repeat Yourself) принципа
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment