[Solved]-Can Django admin handle a one-to-many relationship via related_name?

3👍

👤Matt

1👍

you can set the initial values in the init method of the form.

Refer to them as self.fields[‘manager_staff’].initial.

Like so:

class PersonAdminForm(forms.ModelForm):    
    manager_staff = forms.ModelMultipleChoiceField(
                        queryset=Person.objects.all(),
                        required=False,
                        )

    def __init__(self, *args, **kwargs):
        super(PersonAdminForm, self).__init__(*args, **kwargs)
        if self.instance.id is not None:
            selected_items = [ values[0] for values in Person.objects.filter(
                               #whatever your filter criteria is
                              ) ]
            self.fields['manager_staff'].initial = selected_items

0👍

This might end up being one of those limitations. Here’s what I managed to come up with. It’s possible to create a custom Form and pass that to the admin site to use for rendering a page. However, what I couldn’t get was for it to properly show the initial value properly. Perhaps someone better at Forms can come along with some uber-cool-secret-meta API that I don’t know about and fix this.

models.py

class Person(models.Model):
    # ...
    manager = models.ForeignKey('self', related_name='staff', null=True, blank=True)

    def manager_staff(self):
        return self.manager.staff.all()

admin.py

class PersonAdminForm(forms.ModelForm):    
    manager_staff = forms.ModelMultipleChoiceField(
                        initial='manager_staff',       # Can't get this to work
                        queryset=Person.objects.all(),
                        required=False,
                        )

    class Meta:
        model = Person

class PersonAdmin(admin.ModelAdmin):    
    form = PersonAdminForm

    def save_model(self, request, obj, form, change):
        for id in form.data.getlist('manager_staff'):
            # This is kind of a weak way to do this, but you get the idea...
            p = Person.objects.get(id=id)
            p.manager = obj.manager
            p.save()

        super(PersonAdmin, self).save_model(request, obj, form, change)

admin.site.register(Person, PersonAdmin)

0👍

Here’s what I came up with based on the answers from T. Stone and nicoechaniz. The initial data is provided in much the same way as nicoechaniz does it. For the saving you have to be careful do deal with the case of a completely new Person as well as editing an existing Person. That’s what makes the save() method complicated, depending on non-publicized parts of the ModelForm API. Really this should be built into Django. I imagine it’s a commonly sought after feature.

class PersonAdminForm(forms.ModelForm):
    staff = forms.ModelMultipleChoiceField(
                        queryset=Person.objects.all(),
                        required=False,
                        )
    class Meta:
        model = Person

    def __init__(self, *args, **kwargs):
        super(PersonAdminForm, self).__init__(*args,**kwargs)

        if self.instance.pk is not None:
            self.initial['staff'] = [values[0] for values in self.instance.staff.values_list('pk')]

    def save(self, commit=True):
        instance = super(PersonAdminForm, self).save(commit)

        def save_m2m():
            instance.staff = self.cleaned_data['staff']

        if commit:
            save_m2m()
        elif hasattr(self, 'save_m2m'):
            save_old_m2m = self.save_m2m

            def save_both():
                save_old_m2m()
                save_m2m()

            self.save_m2m = save_both
        else:
            self.save_m2m = save_m2m

        return instance

    save.alters_data = True

class PersonAdmin(admin.ModelAdmin):
    form = PersonAdminForm
👤Rowan

Leave a comment