Функция inlineformset_factory

Функция inlineformset_factory применяется для создания формсета, который позволяет одновременно редактировать несколько объектов дочерней модели, связанных с одним родительским объектом через внешний ключ. Эта функция возвращает класс формсета, который наследуется от BaseInlineFormSet и предоставляет удобный интерфейс для работы с связанными данными. Основные параметры функции включают родительскую модель, дочернюю модель, количество дополнительных пустых форм и максимальное количество форм.

Синтаксис

pythonfrom django.forms import inlineformset_factory

InlineFormSet = inlineformset_factory(
  parent_model,
  model,
  form=ModelForm,
  formset=BaseInlineFormSet,
  fk_name=None,
  fields=None,
  exclude=None,
  extra=3,
  can_delete=True,
  can_order=False,
  max_num=None,
  min_num=None,
  validate_max=False,
  validate_min=False,
  absolute_max=None,
  can_delete_extra=True,
  renderer=None,
  widgets=None,
  localized_fields=None,
  labels=None,
  help_texts=None,
  error_messages=None,
  field_classes=None,
  factory_kwargs=None,
)

Обязательными аргументами являются parent_model и model. Все остальные параметры опциональны и позволяют гибко настраивать поведение формсета.

Пример

Рассмотрим простой пример работы с inlineformset_factory. У нас есть модели Author и Book, где книга связана с автором через внешний ключ. Создадим формсет для редактирования книг конкретного автора:

pythonfrom django.db import models
from django.forms import inlineformset_factory
from myapp.models import Author, Book

# Модели
class Author(models.Model):
  name = models.CharField(max_length=100)

class Book(models.Model):
  title = models.CharField(max_length=200)
  author = models.ForeignKey(Author, on_delete=models.CASCADE)

# Создаём формсет для модели Book с привязкой к Author
BookFormSet = inlineformset_factory(
  Author,
  Book,
  fields=['title'],
  extra=2,
  can_delete=True
)

# Получаем автора из базы данных
author = Author.objects.get(pk=1)

# Создаём формсет с данными существующего автора
formset = BookFormSet(instance=author)

# Выводим количество форм в формсете
print(f"Number of forms: {len(formset)}")

Результат выполнения кода:

python"Number of forms: 3"

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

Пример

Теперь рассмотрим, как обрабатывать данные, переданные методом POST, в представлении. Используем те же модели и формсет для сохранения изменённых данных:

pythonfrom django.shortcuts import render, redirect
from django.forms import inlineformset_factory
from myapp.models import Author, Book

def edit_author_books(request, author_id):
  author = Author.objects.get(pk=author_id)
  BookFormSet = inlineformset_factory(
    Author,
    Book,
    fields=['title'],
    extra=2,
    can_delete=True
  )

  if request.method == 'POST':
    formset = BookFormSet(request.POST, instance=author)
    if formset.is_valid():
      formset.save()
      return redirect('author_detail', author_id=author.id)
  else:
    formset = BookFormSet(instance=author)

  return render(request, 'edit_books.html', {'formset': formset})

В этом примере при GET-запросе мы создаём формсет с данными автора. При POST-запросе передаём данные в формсет, проверяем их валидность и сохраняем. Метод save формсета автоматически обработает создание, обновление и удаление связанных объектов в зависимости от переданных данных.

Пример

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

pythonfrom django import forms
from django.forms import inlineformset_factory
from myapp.models import Author, Book

# Кастомная форма для книги
class BookForm(forms.ModelForm):
  class Meta:
    model = Book
    fields = ['title', 'publication_year']

  def clean_title(self):
    title = self.cleaned_data.get('title')
    if len(title) < 3:
      raise forms.ValidationError("Title is too short!")
    return title

# Создаём формсет с кастомной формой
BookFormSet = inlineformset_factory(
  Author,
  Book,
  form=BookForm,
  extra=1,
  can_delete=False,
  max_num=5
)

author = Author.objects.get(pk=1)
formset = BookFormSet(instance=author)

# Получаем словари с данными форм
for form in formset:
  print(form.cleaned_data if form.is_valid() else "Invalid form")

Результат выполнения кода для валидных форм:

python"{'title': 'Django for Beginners', 'publication_year': 2023}"
"{'title': 'Advanced Django', 'publication_year': 2024}"
"{'title': '', 'publication_year': None}"

В этом примере мы использовали кастомную форму с валидацией поля title и установили ограничение на максимальное количество форм до пяти. Параметр can_delete отключён, поэтому удаление записей через формсет недоступно.

Смотрите также

  • функция modelformset_factory,
    которая создаёт формсет для модели без привязки к родительскому объекту
  • функция formset_factory,
    которая создаёт формсет из обычных форм Django
  • класс save,
    который сохраняет данные формы модели в базу данных
  • атрибут instance,
    который связывает форму с конкретным объектом модели