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!
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With