简体   繁体   中英

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?

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)

You don't give a reference for this claim:

the Django documentation (probably incorrectly) says "you must tell Django's admin to not display this widget".

so I can only refer to the current (1.10) documentation for Django . It currently says of ManyToMany fields in the admin :

Django displays an admin widget for a many-to-many field on the model that defines the relation (in this case, Group ). If you want to use an inline model to represent the many-to-many relationship, you must tell Django's admin to not display this widget - otherwise you will end up with two widgets on your admin page for managing the relation.

So, in response to your correct statement:

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.

the reason is that the many-to-many relationship has to be defined somewhere ; you have chosen to define it on the Group model, so that determines the default admin behaviour. If you want to move it, then you'll need to do a database migration to make that happen.

If, on the other hand, you want this documented behaviour to be different without changing your use of it — you don't seem to be asking a question that fits at StackOverflow. Better to report a bug with the program at the project's bug tracker , asking for a change in the software's behaviour.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM