Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django admin dynamically set changeform initial data

I'm implementing a m2m relationship through an intermediate model using the default m2m widget. I have the Person and Project models related using the Membership model.

So far I've succeeded at displaying the default m2m widget in the Person change form and creating the intermediate model instances correctly, but my problem is populating the widget when a Person is being modified.

This is the form class I'm using with the PersonAdmin:

class PersonForm(forms.ModelForm):
    projects = forms.ModelMultipleChoiceField(models.Project.objects.all(),
                                              widget=widgets.FilteredSelectMultiple(
                                                  verbose_name="Projects",
                                                  is_stacked=False,
                                                  attrs={'rows':'10'}))
    projects.required = False
    class Meta:
        model = models.Person
        fields = ['name', 'last_name', 'personal_id_number',
                  'personal_id_type', 'administrative_viability',
                  'observations']

    def save(self, commit=True):
        ret = super(PersonForm, self).save(commit)
        for p in self.cleaned_data['projects']:
            models.Membership.objects.create(person=self.instance, project=p)
        return ret

And the PersonAdmin itself:

class PersonAdmin(admin.ModelAdmin):
    form = PersonForm

    def get_changeform_initial_data(self, request):
        initial = super(PersonAdmin, self).get_changeform_initial_data(request)
        initial['projects'] = models.Person.get(pk=initial['person']).project_set.all()
        return initial

I tried setting the initial value of projects in the method get_changeform_initial_data like that, but it doesn't work. Overall it looks like it's being ignored, as if I'm not overriding it properly.

Any help will be greatly appreciated!

like image 722
jflorezf Avatar asked Feb 12 '23 15:02

jflorezf


2 Answers

This question gave me the idea of overriding the __init__ method of my PersonForm:

def __init__(self, *args, **kwargs):
    if 'instance' in kwargs:
        person = kwargs['instance']
        initial = {'projects': person.project_set.all()}
        kwargs['initial'] = initial
    super(PersonForm, self).__init__(*args, **kwargs)

I still don't know why overriding get_changeform_initial_data wasn't working.

like image 62
jflorezf Avatar answered Feb 18 '23 19:02

jflorezf


get_changeform_initial_data is only called if it's not a change. I know this makes no sense. I suspect it's a bug.

See django/contrib/admin/options.py from line 1573, which is the only call to this method in the whole of Django:

            if add:
                initial = self.get_changeform_initial_data(request)
                form = ModelForm(initial=initial)
                formsets, inline_instances = self._create_formsets(request, form.instance, change=False)
            else:
                form = ModelForm(instance=obj)
                formsets, inline_instances = self._create_formsets(request, obj, change=True)

Update: Looks like it's deliberate. I'll ask the developers why it works like this.

like image 40
Marnanel Thurman Avatar answered Feb 18 '23 20:02

Marnanel Thurman