Функция modelformset_factory

Функция modelformset_factory применяется для динамического создания класса набора форм, связанного с моделью Django. Она принимает модель и набор параметров, возвращая класс BaseModelFormSet. Полученный набор форм позволяет обрабатывать несколько экземпляров модели в одной форме, что удобно для массового редактирования, добавления или удаления записей. В отличие от обычных форм, где работа идет с одной записью, набор форм дает возможность изменять сразу несколько объектов.

Основные параметры функции:

Все параметры передаются в виде именованных аргументов, при этом обязательными являются только model и хотя бы один из параметров fields или exclude.

Синтаксис

pythonfrom django.forms import modelformset_factory

ModelFormSet = modelformset_factory(
  model,
  fields='__all__',
  extra=1,
  max_num=None,
  min_num=None,
  can_delete=False
)

Пример

Допустим, у нас есть модель Article для статей. Создадим набор форм для редактирования заголовков и статусов статей:

pythonfrom django.db import models

class Article(models.Model):
  title = models.CharField(max_length=200)
  status = models.CharField(max_length=20)
  created_at = models.DateTimeField(auto_now_add=True)

  def __str__(self):
    return self.title

Создадим набор форм и выведем формы для всех существующих статей:

pythonfrom django.forms import modelformset_factory
from myapp.models import Article

ArticleFormSet = modelformset_factory(
  model=Article,
  fields=['title', 'status']
)

queryset = Article.objects.all()
formset = ArticleFormSet(queryset=queryset)

for form in formset:
  print(form.instance.title, form.instance.status)

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

python"article 1 published"
"article 2 draft"
"article 3 published"

Пример

Набор форм удобно использовать для массового обновления данных. Создадим набор форм с возможностью добавления новых статей и удаления существующих:

pythonfrom django.forms import modelformset_factory
from myapp.models import Article

ArticleFormSet = modelformset_factory(
  model=Article,
  fields=['title', 'status'],
  extra=2,
  can_delete=True,
  max_num=10
)

queryset = Article.objects.filter(status='draft')
formset = ArticleFormSet(queryset=queryset)

if formset.is_valid():
  instances = formset.save()
  print(f"Saved {len(instances)} articles")
else:
  print("Formset is not valid")

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

python"Saved 2 articles"

Пример

Используем пользовательскую форму и переопределим поведение набора форм для кастомной валидации:

pythonfrom django import forms
from django.forms import modelformset_factory
from myapp.models import Article

class CustomArticleForm(forms.ModelForm):
  class Meta:
    model = Article
    fields = ['title', 'status']

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

class CustomArticleFormSet(forms.BaseModelFormSet):
  def clean(self):
    super().clean()
    titles = []
    for form in self.forms:
      if form.cleaned_data and 'title' in form.cleaned_data:
        title = form.cleaned_data['title']
        if title in titles:
          raise forms.ValidationError(
            "Duplicate titles are not allowed"
          )
        titles.append(title)

ArticleFormSet = modelformset_factory(
  model=Article,
  form=CustomArticleForm,
  formset=CustomArticleFormSet,
  extra=1,
  can_delete=True
)

queryset = Article.objects.all()
formset = ArticleFormSet(queryset=queryset)

if formset.is_valid():
  formset.save()
  print("All articles saved successfully")
else:
  print("Formset errors:", formset.errors)

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

python"All articles saved successfully"

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

  • функция formset_factory,
    которая создает набор форм на основе обычной формы
  • функция inlineformset_factory,
    которая создает набор форм для работы с внешними ключами
  • класс ModelForm,
    который используется для создания форм на основе моделей
  • метод save,
    который сохраняет данные формы в базу данных