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

Что такое ModelForm в Django?

1.3 Junior🔥 161 комментариев
#Django

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

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

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

ModelForm в Django

ModelForm — это один из самых мощных инструментов Django, который автоматически создаёт HTML-форму на основе модели базы данных. Это экономит тонны кода и делает разработку быстрой.

Суть проблемы, которую решает ModelForm

Представь, что у тебя есть модель User в базе данных:

from django.db import models

class User(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField()
    age = models.IntegerField()
    bio = models.TextField()
    is_active = models.BooleanField(default=True)

Без ModelForm пришлось бы писать форму вручную:

from django import forms

class UserForm(forms.Form):
    name = forms.CharField(max_length=100, required=True)
    email = forms.EmailField(required=True)
    age = forms.IntegerField(required=True)
    bio = forms.CharField(widget=forms.Textarea)
    is_active = forms.BooleanField(required=False)

Это дублирование! Те же поля описаны дважды. ModelForm решает эту проблему.

Базовое использование ModelForm

from django import forms
from .models import User

class UserModelForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ['name', 'email', 'age', 'bio', 'is_active']

Вот и всё! Django автоматически:

  • Создаст поля формы на основе типов полей модели
  • Применит валидацию
  • Установит правильные виджеты (текстовое поле, textarea и т.д.)

Использование в представлении (View)

from django.shortcuts import render, redirect, get_object_or_404
from django.views import View
from .models import User
from .forms import UserModelForm

# Функциональное представление
def create_user(request):
    if request.method == 'POST':
        form = UserModelForm(request.POST)
        if form.is_valid():
            form.save()  # Автоматически сохранит в БД!
            return redirect('user_list')
    else:
        form = UserModelForm()
    
    return render(request, 'create_user.html', {'form': form})

# Или обновление
def update_user(request, user_id):
    user = get_object_or_404(User, id=user_id)
    if request.method == 'POST':
        form = UserModelForm(request.POST, instance=user)
        if form.is_valid():
            form.save()
            return redirect('user_detail', user_id=user_id)
    else:
        form = UserModelForm(instance=user)  # Заполняет форму текущими данными
    
    return render(request, 'edit_user.html', {'form': form, 'user': user})

Обрати внимание на form.save() — это сохраняет данные прямо в БД!

В шаблоне (HTML)

<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Сохранить</button>
</form>

Django автоматически отрендерит все поля с правильными типами input.

Кастомизация ModelForm

class UserModelForm(forms.ModelForm):
    # Добавляем кастомное поле
    subscribe = forms.BooleanField(
        required=False,
        label="Подписаться на новости"
    )
    
    class Meta:
        model = User
        fields = ['name', 'email', 'age', 'bio']
        labels = {
            'name': 'Полное имя',
            'email': 'Email адрес',
        }
        widgets = {
            'name': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': 'Введи имя'
            }),
            'bio': forms.Textarea(attrs={
                'rows': 4,
                'class': 'form-control'
            }),
        }
        help_texts = {
            'age': 'Должно быть от 18 до 120 лет'
        }
    
    def clean_age(self):
        age = self.cleaned_data.get('age')
        if age and (age < 18 or age > 120):
            raise forms.ValidationError("Возраст должен быть от 18 до 120")
        return age
    
    def clean_email(self):
        email = self.cleaned_data.get('email')
        # Проверяем, что email уникален (исключая текущий объект)
        if User.objects.filter(email=email).exclude(pk=self.instance.pk).exists():
            raise forms.ValidationError("Этот email уже используется")
        return email

Что такое cleaned_data?

Это словарь валидированных и очищенных данных:

def create_user(request):
    if request.method == 'POST':
        form = UserModelForm(request.POST)
        if form.is_valid():
            # cleaned_data содержит безопасные данные
            print(form.cleaned_data)  # {'name': '...', 'email': '...', ...}
            form.save()

Выбор полей: fields vs exclude

class Meta:
    model = User
    # Вариант 1: явно указываем какие поля
    fields = ['name', 'email', 'bio']

class Meta:
    model = User
    # Вариант 2: исключаем ненужные поля
    exclude = ['created_at', 'updated_at', 'is_staff']

Лучше использовать fields, потому что это явно показывает, какие поля редактируются.

Сохранение с доп. обработкой

def create_user(request):
    if request.method == 'POST':
        form = UserModelForm(request.POST)
        if form.is_valid():
            user = form.save(commit=False)  # Не сохраняем ещё в БД
            user.created_by = request.user  # Добавляем текущего пользователя
            user.save()  # Теперь сохраняем
            return redirect('user_detail', user_id=user.id)

Класс-представление (Class-Based View)

Django предоставляет встроенные CBV для ModelForm:

from django.views.generic import CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import User
from .forms import UserModelForm

class UserCreateView(CreateView):
    model = User
    form_class = UserModelForm
    template_name = 'create_user.html'
    success_url = reverse_lazy('user_list')

class UserUpdateView(UpdateView):
    model = User
    form_class = UserModelForm
    template_name = 'edit_user.html'
    pk_url_kwarg = 'user_id'
    success_url = reverse_lazy('user_list')

Even better! Django сам обработает POST и GET запросы.

Валидация на разных уровнях

class UserModelForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ['email']
    
    # Валидация отдельного поля
    def clean_email(self):
        email = self.cleaned_data.get('email')
        if not email.endswith('@company.com'):
            raise forms.ValidationError("Используй корпоративный email")
        return email
    
    # Валидация всей формы (несколько полей)
    def clean(self):
        cleaned_data = super().clean()
        password = cleaned_data.get('password')
        password_confirm = cleaned_data.get('password_confirm')
        
        if password != password_confirm:
            raise forms.ValidationError("Пароли не совпадают")
        
        return cleaned_data

Ошибки в форме

<form method="post">
    {% csrf_token %}
    
    {% if form.non_field_errors %}
        <div class="alert">
            {{ form.non_field_errors }}
        </div>
    {% endif %}
    
    {% for field in form %}
        <div>
            {{ field.label_tag }}
            {{ field }}
            {% if field.errors %}
                <span class="error">{{ field.errors }}</span>
            {% endif %}
        </div>
    {% endfor %}
    
    <button type="submit">Сохранить</button>
</form>

Преимущества ModelForm

  1. Автоматическая валидация — используется валидация модели
  2. Меньше кода — не нужно писать поля вручную
  3. Синхронизация — если модель меняется, форма автоматически обновляется
  4. Встроенное сохранение — form.save() сохраняет прямо в БД
  5. Интеграция с моделью — работает с отношениями (ForeignKey, ManyToMany)

Работа с отношениями

class Post(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    tags = models.ManyToManyField(Tag)

class PostModelForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'author', 'tags']

Django автоматически создаст выпадающий список для author и мультиселект для tags!

Вывод

ModelForm — это must-have инструмент в Django. Он экономит кучу кода, обеспечивает валидацию и делает код более maintainable. Правильное использование ModelForm — признак опытного Django-разработчика.