How to set initial values ​​for ModelForm when instance is also specified

It seems that if a ModelForm is given an instance, it ignores any values ​​that you provide for initial , and instead sets it to the instance value - even if this instance is an empty model record.

Is there a way to create a form with an instance and set the source data?

I need this because I save related records and they do not display correctly if ModelForm did not provide an instance at creation.

I am sure that the answer to this is simple, and I will just miss something obvious.

Here is the relevant code:

in view:

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

where form_class RegistrationForm and then in the registration form:

 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) 

Then, when I call this field, the associated fields are empty.

+4
source share
3 answers

Thought about it after a few searches.

You must set the initial value before calling super .

So, instead of scrolling through self.fields.keys() , I had to enter a list of the fields I wanted and looped over them:

 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 rightly points out that before you call super, you do not have self.fields . I am sure this will work:

 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) 

In this version, we use the initial argument to pass values ​​to. It is also written so that if we already have a value in the source for this field, we do not overwrite it.

+4
source

It seems to me that you can look for a related form. Not quite sure, I'm trying to solve a similar problem:

Django formats can be created with two arguments that control this kind of thing. As far as I understand:

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

initial will set possible values ​​for the field-like parameter queryset-data will set the actual (or selected) form values ​​and create the associated form. Perhaps this is what you want. Another, tangent, may seem interesting to you - consider the factory method, rather than the constructor, I think the syntax is more natural:

 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) 
+1
source

You can also pass additional variables to the class when it is initialized. The values ​​you pass can then override the initial or POST data.

 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) 
0
source

Source: https://habr.com/ru/post/1497250/


All Articles