JavaRush /Курсы /Модуль 3: Django /Создание форм для связанных моделей

Создание форм для связанных моделей

Модуль 3: Django
13 уровень , 7 лекция
Открыта

Представьте, что вы создаёте блог, где есть посты и категории. Каждый пост относится к одной или нескольким категориям (связь ManyToMany). Блогеру будет нужен интерфейс, чтобы не только добавить новый пост, но и отредактировать категории прямо в той же форме. Как это сделать? Использовать формы для связанных моделей!

В реальных проектах такие формы используются везде, где есть необходимость работать с данными из нескольких таблиц/моделей базы данных. Это ведь гораздо удобнее для пользователей — один интерфейс для всего.

Подготовка: создадим модели и свяжем их

Перед тем как начать работу с формами, сначала настроим модели. В качестве примера будем работать с блогом.

from django.db import models

class Category(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    categories = models.ManyToManyField(Category, related_name='posts')

    def __str__(self):
        return self.title

Здесь у нас две модели: Category для категорий и Post для постов. Посты могут быть связаны с несколькими категориями через поле categories (связь типа ManyToMany).

Подход №1: Работаем с отдельными формами

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

Форма для создания постов

from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'content', 'categories']

Форма для создания категорий

from django import forms
from .models import Category

class CategoryForm(forms.ModelForm):
    class Meta:
        model = Category
        fields = ['name']

Каждая модель здесь имеет свою отдельную форму. Поле categories автоматически отображается в форме PostForm как MultipleChoiceField, где пользователь сможет выбирать из существующих категорий.

Шаги в представлениях

Теперь нужно написать представления, чтобы обработать обе формы. Работу с категориями и постами можно разделить.

from django.shortcuts import render, redirect
from .models import Post, Category
from .forms import PostForm, CategoryForm

def create_post(request):
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('post_list')  # Перенаправляем на список постов
    else:
        form = PostForm()
    return render(request, 'blog/create_post.html', {'form': form})

def create_category(request):
    if request.method == 'POST':
        form = CategoryForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('category_list')  # Перенаправляем на список категорий
    else:
        form = CategoryForm()
    return render(request, 'blog/create_category.html', {'form': form})

В шаблонах это будет выглядеть примерно так:

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

Этот подход удобен, но требует, чтобы пользователь сначала создал категории, а затем добавил посты и выбрал соответствующие категории. Хотите большего удобства? Давайте посмотрим, как сделать одну форму, которая будет поддерживать всё.

Подход №2: Inline формы для связанных моделей

Когда нужно дать пользователю возможность редактировать сразу и пост, и связанные категории, на помощь приходят inline формы. Django предоставляет мощный инструмент под названием inlineformset_factory.

Создание Inline FormSet

from django.forms import inlineformset_factory
from .models import Post, Category

CategoryInlineFormSet = inlineformset_factory(
    Post,  # Родительская модель
    Category,  # Связанная модель
    fields=('name',),  # Поля, которые редактируются
    extra=1,  # Количество пустых форм для новых объектов
    can_delete=True  # Возможность удалять связанные объекты
)

Здесь inlineformset_factory создаёт форму, которая позволяет редактировать категории, связанные с конкретным постом.

Представление для работы с Inline FormSet

Теперь нужно написать представление, чтобы отобразить и обработать нашу inline форму.

from django.shortcuts import render, get_object_or_404, redirect
from .models import Post
from .forms import PostForm
from .forms import CategoryInlineFormSet

def edit_post(request, pk):
    post = get_object_or_404(Post, pk=pk)
    form = PostForm(instance=post)
    formset = CategoryInlineFormSet(instance=post)

    if request.method == 'POST':
        form = PostForm(request.POST, instance=post)
        formset = CategoryInlineFormSet(request.POST, instance=post)
        if form.is_valid() and formset.is_valid():
            form.save()
            formset.save()
            return redirect('post_list')  # Перенаправляем на список постов

    return render(request, 'blog/edit_post.html', {'form': form, 'formset': formset})

Шаблон с основной формой и inline формами

Теперь создадим шаблон, где обе формы будут отображаться вместе.

<form method="post">
    {% csrf_token %}
    {{ form.as_p }}

    <h3>Категории:</h3>
    {{ formset.management_form }}
    {% for form in formset %}
        {{ form.as_p }}
    {% endfor %}

    <button type="submit">Сохранить</button>
</form>

Как это работает?

  1. PostForm позволяет редактировать данные поста (title, content).
  2. CategoryInlineFormSet позволяет редактировать категории, связанные с постом.
  3. Если пользователь добавляет категорию или удаляет существующую, это автоматически фиксируется в базе.

Подход №3: Использование виджетов для ManyToMany

Если вам нужно простое решение для связи ManyToMany, можно использовать встроенные виджеты Django. Например, виджет CheckboxSelectMultiple позволяет отображать связанные объекты в виде списка чекбоксов.

Ваша форма будет выглядеть так:

from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'content', 'categories']
        widgets = {
            'categories': forms.CheckboxSelectMultiple()
        }

Теперь в шаблоне поле categories будет представлено списком чекбоксов, где пользователь сможет выбрать несколько категорий.

Практическое задание

  1. Реализуйте модель авторов Author, связанных с постами через поле ForeignKey.
  2. Создайте форму, которая позволит редактировать посты и связанные с ними данные об авторах.
  3. Настройте представление и шаблон для работы с этой формой.

Такой подход даст вам реальное понимание, как работают формы для связанных моделей и какие из них удобнее использовать в различных сценариях.

1
Задача
Модуль 3: Django, 13 уровень, 7 лекция
Недоступна
Создание формы для модели с ManyToManyField
Создание формы для модели с ManyToManyField
1
Задача
Модуль 3: Django, 13 уровень, 7 лекция
Недоступна
Интеграция с базовыми связями
Интеграция с базовыми связями
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Евгений Уровень 70
18 сентября 2025
Подход №2 - inlineformset_factory Код не взлетел - упал с ошибкой:
ValueError: 'app.Category' has no ForeignKey to 'app.Post'.
Начал копать. Выснилось, что inlineformset_factory работает только с ForeignKey, потому что он предназначен для редактирования связанных объектов, где дочерняя модель имеет чёткую привязку к родителю через FK. А ведь можно же было об этом и упомянуть в материале. UPD: Про inlineformset_factory все встанет на свои места в след.лекции