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

Jordan Reiter picture Jordan Reiter · Aug 15, 2013 · Viewed 30k times · Source

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 that instance is an empty model record.

Is there any way to create a form with an instance and have it set initial data?

I need it because I'm saving related records and they don't appear to save correctly unless the ModelForm is given an instance when created.

I'm sure the answer to this is straightforward and I'm just missing something obvious.

Here is the relevant code:

in the view:

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

where form_class is 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 the field, the related fields are empty.

Answer

Jordan Reiter picture Jordan Reiter · Aug 15, 2013

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.