[Fixed]-How to set initial values for a ModelForm when instance is also given

9👍

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.

2👍

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)

1👍

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)
👤Tom

0👍

Use ModelAdmin.get_changeform_initial_data. For example, if you add initial data for form field "report_datetime"

    def get_changeform_initial_data(self, request):
        initial_data = super().get_changeform_initial_data(request)
        initial_data.update(report_datetime=<my_initial_datetime>)
        return initial_data

Works for 3.2+. I’m not sure about older versions.

See django docs

👤erikvw

Leave a comment