Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django admin many-to-many reversed?

The answer to question Django admin ManyToMany inline "has no ForeignKey to" error refers to the Django Admin documentation. The models given there are:

class Person(models.Model):
    name = models.CharField(max_length=128)

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, related_name='groups')

and the inline admin classes are:

class MembershipInline(admin.TabularInline):
    model = Group.members.through

class PersonAdmin(admin.ModelAdmin):
    inlines = [MembershipInline,]

class GroupAdmin(admin.ModelAdmin):
    inlines = [MembershipInline,]
    exclude = ('members',)

... which allows group membership to be managed from the Person page but not from the Group page. But what if the administrator wants to manage members only from the Group page? Getting rid of the exclude line would allow both pages to manage the relationship, but the Django documentation (probably incorrectly) says "you must tell Django’s admin to not display this widget". What they probably mean is that you "should" tell Django's admin not to display it - nothing bad will happen if you don't, but it's redundant.

So without changing the models, is it possible to exclude the membership widget from the Person page instead of from the Group page? Both obvious attempts:

class PersonAdmin(admin.ModelAdmin):
    inlines = [MembershipInline,]
    exclude = ('Group.members',)

and

class PersonAdmin(admin.ModelAdmin):
    inlines = [MembershipInline,]
    exclude = ('groups',)

(the second using the related_name from the model) fail with the error:

'PersonAdmin.exclude' refers to field 'groups' that is missing from the form.

Yes, the model could be changed to put the ManyToManyField under Person. But since it is a symmetric relationship, there is no logical reason why it could not be managed from either Person or Group (but not both) without having to change the database schema. Can Django Admin manage group membership from the group page and exclude it from the person page?

like image 247
Dave Avatar asked Aug 14 '15 19:08

Dave


1 Answers

what if the administrator wants to manage members only from the Group page?

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    pass

@admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
    pass

Django by default shows a Person m2m widget in the GroupAdmin. You correctly use the through model to get inlines, but the inlines are a separate definition not affected by the exclude. EDIT: Another simple way to put it is that you only specify the inlines on the Admin where you want them, no need to specify them on the opposite side's Admin.

Using inlines:

from core.models import Group, Person

class MembershipInline(admin.TabularInline):
    model = Group.members.through

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    pass

@admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
    inlines = [MembershipInline, ]
    exclude = ('members',)

(tested on Django 3.0.3)

like image 175
Jura Brazdil Avatar answered Oct 14 '22 17:10

Jura Brazdil