Поле ModelChoiceField

Поле ModelChoiceField предназначено для создания выпадающего списка, где каждый вариант представляет собой объект модели. Оно принимает аргумент queryset - набор объектов, из которых пользователь может выбрать один. Это поле автоматически валидирует переданные данные, проверяя наличие выбранного объекта в указанном наборе запросов. При отправке формы значение поля преобразуется в объект модели, а не в его первичный ключ.

Синтаксис

pythonfrom django import forms
from .models import Category

class MyForm(forms.Form):
  category = forms.ModelChoiceField(queryset=Category.objects.all())

Параметры

Основной параметр - queryset, задающий набор объектов для выбора. Дополнительные параметры включают empty_label для добавления пустого пункта, to_field_name для указания поля модели, используемого в качестве значения, и стандартные параметры полей форм (например, required, initial, label).

Пример . Использование в форме

Создадим форму для выбора категории статьи с помощью ModelChoiceField:

pythonfrom django import forms
from .models import Category, Article

class ArticleForm(forms.Form):
  title = forms.CharField(max_length=100)
  content = forms.CharField(widget=forms.Textarea)
  category = forms.ModelChoiceField(queryset=Category.objects.all())

При рендеринге форма создаст выпадающий список со всеми категориями из базы данных. После отправки формы значение поля category будет объектом модели Category, а не его ID.

Пример . Обработка данных

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

pythonfrom django.shortcuts import render
from .forms import ArticleForm

def create_article(request):
  if request.method == 'POST':
    form = ArticleForm(request.POST)
    if form.is_valid():
      title = form.cleaned_data['title']
      content = form.cleaned_data['content']
      category = form.cleaned_data['category']
      
      # category - объект Category
      print(f"Title: {title}")
      print(f"Category: {category.name}")
  else:
    form = ArticleForm()
    
  return render(request, 'create_article.html', {'form': form})

Валидация поля выполняется автоматически: если выбранный объект не существует в queryset, будет вызвана ошибка валидации.

Фильтрация queryset

Используйте метод filter, чтобы динамически ограничивать набор доступных объектов:

pythonfrom django import forms
from .models import Article, Category

class ArticleForm(forms.Form):
  title = forms.CharField(max_length=100)
  category = forms.ModelChoiceField(
    queryset=Category.objects.filter(is_active=True)
  )

В этом примере в выпадающем списке отобразятся только активные категории.

Пустой пункт в выпадающем списке

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

pythonfrom django import forms
from .models import Category

class CategoryForm(forms.Form):
  category = forms.ModelChoiceField(
    queryset=Category.objects.all(),
    empty_label="Выберите категорию"
  )

Если установить empty_label в None, пустой пункт не будет отображаться. При значении required=False выбор пустого пункта допустим, и поле вернет None.

Использование to_field_name

Параметр to_field_name позволяет указать поле модели, которое будет использоваться в качестве значения выпадающего списка:

pythonfrom django import forms
from .models import Article

class ArticleForm(forms.Form):
  article = forms.ModelChoiceField(
    queryset=Article.objects.all(),
    to_field_name="slug"
  )

Теперь значениями в выпадающем списке будут не первичные ключи, а значения поля slug модели Article.

Динамическая настройка queryset

Для динамической фильтрации queryset в зависимости от пользователя или других условий переопределите метод __init__ формы:

pythonfrom django import forms
from .models import Category

class ArticleForm(forms.Form):
  title = forms.CharField(max_length=100)
  category = forms.ModelChoiceField(queryset=Category.objects.all())
  
  def __init__(self, user, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.fields['category'].queryset = Category.objects.filter(
      author=user
    )

В этом случае доступные категории будут зависеть от переданного пользователя.

Результат валидации

Если пользователь выберет несуществующий объект, поле вызовет ошибку ValidationError. Форма будет содержать ошибку в поле category:

pythonfrom django import forms
from .models import Category

form = CategoryForm({'category': 999})  # Несуществующий ID
if not form.is_valid():
  print(form.errors['category'])

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

python"Select a valid choice. 999 is not one of the available choices."

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

  • класс Form,
    основной класс для создания форм
  • класс ModelForm,
    форма, связанная с моделью базы данных
  • поле ChoiceField,
    выбор из фиксированного списка вариантов
  • поле ModelMultipleChoiceField,
    выбор нескольких объектов модели