Как установить начальные значения для ModelForm, когда также указан экземпляр

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

Есть ли способ создать форму с экземпляром и задать начальные данные?

Мне это нужно, потому что я сохраняю связанные записи, и они не сохраняются правильно, если ModelForm не получает экземпляр при создании.

Я уверен, что ответ на этот вопрос прост, и я просто упускаю что-то очевидное.

Вот соответствующий код:

в представлении:

form = form_class(person=person, conference=conference, initial=initial, instance=registration)

где form_class равен RegistrationForm и далее в регистрационной форме:

class RegisterForm(forms.ModelForm):
    ... fields here ...

    def __init__(self, *args, **kwargs):
        ... other code ...
        self.person = kwargs.pop('person')
        super(RegisterForm, self).__init__(*args, **kwargs)
        for key, in self.fields.keys():
            if hasattr(self.person, key):
                self.fields[k].initial = getattr(self.person, key)

Затем, когда я вызываю поле, связанные поля пусты.


person Jordan Reiter    schedule 15.08.2013    source источник
comment
Если кто-то все еще сталкивается с этим вопросом, посмотрите здесь: stackoverflow.com/a/26887842/11983534   -  person Whale    schedule 16.05.2020
comment
Если кто-то все еще сталкивается с этим вопросом, посмотрите здесь: stackoverflow.com/a/26887842/11983534   -  person Whale    schedule 16.05.2020


Ответы (3)


Выяснил это после небольшого гугления.

Вы должны установить начальное значение перед вызовом super.

Поэтому вместо того, чтобы перебирать self.fields.keys(), мне пришлось ввести список полей, которые я хотел, и вместо этого прокрутить его:

class RegisterForm(forms.ModelForm):
    ... fields here ...
    initial_fields = ['first_name', 'last_name', ... ]

    def __init__(self, *args, **kwargs):
        ... other code ...
        self.person = kwargs.pop('person')
        for key in self.initial_fields:
            if hasattr(self.person, key):
                self.fields[k].initial = getattr(self.person, key)
        super(RegisterForm, self).__init__(*args, **kwargs)

@Daria правильно указывает, что у вас нет self.fields перед вызовом super. Я почти уверен, что это сработает:

class RegisterForm(forms.ModelForm):
    ... fields here ...
    initial_fields = ['first_name', 'last_name', ... ]

    def __init__(self, *args, **kwargs):
        ... other code ...
        initial = kwargs.pop('initial', {})
        self.person = kwargs.pop('person')
        for key in self.initial_fields:
            if hasattr(self.person, key):
                initial[key] = initial.get(key) or getattr(self.person, key)
        kwargs['initial'] = initial
        super(RegisterForm, self).__init__(*args, **kwargs)

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

person Jordan Reiter    schedule 15.08.2013
comment
Но перед вызовом суперконструктора у вас нет self.fields - person Daria; 21.04.2014
comment
Ха, ты прав. Не уверен, как я закончил эту работу… почти полгода назад. Мне нужно посмотреть, смогу ли я найти соответствующий код и как он был реализован. - person Jordan Reiter; 21.04.2014
comment
for key, in self.initial_fields: не является допустимым Python. Если я не ошибаюсь, либо удалите ,, либо добавьте подчеркивание , _. - person Grijesh Chauhan; 18.11.2014

Мне кажется, вы ищете связанную форму. Не совсем уверен, я пытаюсь решить аналогичную проблему:

Формы Django могут быть созданы с двумя аргументами, которые управляют такими вещами. Я так понимаю:

form = MyForm(initial={...}, data={...}, ...)

Initial установит возможные значения для полей — подобно настройке набора запросов — data установит фактические (или выбранные) значения формы и создаст связанную форму. Может быть, это то, что вы хотите. Другой, касательный момент, который может показаться вам интересным, - это рассмотреть фабричный метод, а не конструктор, я думаю, что синтаксис более естественен:

class MyForm(forms.ModelForm):

    ...

    @staticmethod
    def makeBoundForm(user):
        myObjSet = MyObject.objects.filter(some_attr__user=user)
        if len(myObjSet) is not 0:
            data = {'myObject': myObjSet[0]}
        else:
            raise ValueError()
        initial = {'myObject': myObjSet}
        return MyForm(initial=initial, data=data)
person Peter Shannon    schedule 11.06.2015
comment
Не совсем. В этом случае это больше похоже на то, что я пытался предварительно заполнить некоторые поля для одного типа модели полями из другой модели. Например, заполнение полей адреса для заказа на основе существующего адреса для этого пользователя. - person Jordan Reiter; 19.06.2015
comment
Ясно, ну, я думаю, вы все еще можете применить основную мысль, которую я высказал. Эта страница описывает, как вы можете преобразовать модель в словарь в общем виде (на самом деле это не сложная проблема, но вы должны сэкономить время), когда у вас есть словарь, вы можете удалить любые поля, которые вы не хотите устанавливать, и предоставить его как data< /i> аргумент. Я предполагаю, что это альтернатива instance, которая предоставляет больший контроль над используемыми данными инициализации формы. - person Peter Shannon; 21.06.2015

Вы также можете передать дополнительные переменные классу при его инициализации. Передаваемые значения могут затем переопределить начальные данные или данные POST.

class RegisterForm(forms.ModelForm):
    ... fields here ...

    def __init__(self, person, conference, *args, **kwargs):
        ... other code ...
        super(RegisterForm, self).__init__(*args, **kwargs)
        self.fields['person'] = person
        self.fields['conference'] = conference

form = RegisterForm(person, conference, initial=initial, instance=registration)
person Community    schedule 20.01.2015
comment
Если я не читаю это неправильно, это заменяет поля значениями из модели. Я не могу придумать вариант использования, в котором вы действительно хотели бы это сделать. - person Jordan Reiter; 19.06.2015
comment
Исходные данные и данные экземпляра будут переданы в super(RegisterForm, self).__init__(*args, **kwargs), тогда явно переданные значения из представления (или где бы ни создавался экземпляр формы) person и conference переопределяют как initial, так и instance. - person Tom; 19.06.2015
comment
Прочитайте свой код. Вы устанавливаете self.fields['person'] на значение, содержащееся в person. self.fields['person'] было полем; person не было. Возможно, вы имели в виду self.fields['person'].initial = person? Потому что я не могу придумать ни одной ситуации, в которой вы хотели бы заменить одно из полей формы записью. - person Jordan Reiter; 19.06.2015