Предварительное заполнение поля выбора Django ChoiceField внутри набора форм

У меня есть набор форм с полем Runner, которое должно иметь некоторое значение из модели Runner. Я использую виджет select2 для заполнения этого значения: когда пользователь что-то набирает, я использую Ajax для запроса соответствующих строк из базы данных.

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

Если я определяю это поле как ChoiceField с choices=[], открываю форму и выбираю некоторое runner (например, с id=123), то после отправки POST-запроса и попытки инициализировать мой набор форм с FormsetResult(data=request.POST), я получаю ошибку, что предоставленное значение не представлено в списке choices, что является истиной.

Я попытался добавить это значение к choices в конструкторе формы:

def __init__(self, *args, **kwargs):
  runner_id = None
  if self.fields['runner'].initial:
    runner_id = kwargs['initial'].get('runner')
    runner = models.Runner.objects.filter(pk=runner_id).first()
    if runner:
      self.fields['runner'].choices = [('', ''), (runner_id, runner.get_name())]

, но это все равно не работает после отправки формы: initial значения теперь пустые, а в случае наборов форм, POST словарь имеет уродливые имена полей, такие как form-0-runner, что затрудняет извлечение нужного мне значения.

Другой вариант - написать какой-нибудь хак в шаблоне, который будет загружать из БД нужное мне значение:

<select name="form-{{ forloop.counter0 }}-runner" id="id_form-{{ forloop.counter0 }}-runner">
    {% if form.instance.runner %}
        <option value="{{ form.instance.runner.id }}" selected>{{ form.instance.runner.get_name }}</option>
    {% endif %}
</select>

, но это тоже не красиво.

Может быть, вы видите лучший способ достичь этого? Возможно, приложение Django-Select2 поможет мне в этом?

Вам необходимо переопределить метод __init__ в вашей форме

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)

    #if form input is empty
    self.fields['runner'].queryset = Runner.objects.none()

    # if user typed something
    if 'runner' in self.data:
        self.fields['runner'].queryset = Runner.objects.all()

    # if object exists it will set the value of field to previously saved one
    elif self.instance.pk:
        self.fields['runner'].queryset = Runner.objects.all().filter(pk=self.instance.runner.pk)
Вернуться на верх