How to set initial values for a ModelForm when instance is also given

后端 未结 3 1393
庸人自扰
庸人自扰 2021-01-02 06:54

It seems like if a ModelForm is given an instance, it ignores any values you provide for initial and instead sets it to the value of the instance -- even if tha

相关标签:
3条回答
  • 2021-01-02 07:16

    Figured this out after a little bit of googling.

    You have to set the initial value before calling super.

    So instead of looping through self.fields.keys(), I had to type out the list of fields that I wanted and looped through that instead:

    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 you don't have self.fields before calling super. I'm pretty 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 the values in. It's also written so that if we already have a value in initial for that field, we don't overwrite it.

    0 讨论(0)
  • 2021-01-02 07:21

    You can also pass extra variables to the class when initializing it. The values you pass can then override 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 讨论(0)
  • 2021-01-02 07:23

    Sounds to me that you may be looking for a bound form. Not entirely sure, I'm trying to unpick a similar issue:

    Django forms can be instantiated with two arguments which control this kind of thing. As I understand it:

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

    initial will set the possible values for the fields—like setting a queryset—data will set the actual (or selected) values of a form and create a bound form. Maybe that is what you want. Another, tangental, point you might find interesting is to consider a factory method rather than a 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)
    
    0 讨论(0)
提交回复
热议问题