【问题标题】:Django admin many-to-many reversed?Django admin 多对多反转?
【发布时间】:2015-11-08 03:26:54
【问题描述】:

问题Django admin ManyToMany inline "has no ForeignKey to" error 的答案参考了Django Admin 文档。那里给出的模型是:

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')

内联管理类是:

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

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

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

... 允许从“个人”页面而不是“组”页面管理组成员资格。但是,如果管理员只想从“组”页面管理成员怎么办?去掉 exclude 行将允许两个页面管理关系,但是 Django 文档(可能不正确)说“你必须告诉 Django 的管理员不要显示这个小部件”。他们可能的意思是你“应该”告诉 Django 的管理员不要显示它——如果你不这样做不会发生任何不好的事情,但这是多余的。

因此,在不更改模型的情况下,是否可以从“个人”页面而不是“组”页面中排除成员资格小部件?两种明显的尝试:

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

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

(第二个使用模型中的related_name)失败并出现错误:

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

是的,可以更改模型以将ManyToManyField 放在Person 下。但由于它是一种对称关系,所以没有任何逻辑理由不能从 Person 或 Group(但不能同时从两者)进行管理,而无需更改数据库模式。 Django Admin 可以从群组页面管理群组成员资格并将其从人员页面中排除吗?

【问题讨论】:

    标签: python django django-models django-admin


    【解决方案1】:

    如果管理员只想管理群组中的成员怎么办 页面?

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

    默认情况下,Django 在 GroupAdmin 中显示一个 Person m2m 小部件。您正确使用直通模型来获取内联,但内联是不受排除影响的单独定义。编辑:另一种简单的说法是,您只需在 Admin 上指定您想要它们的内联,无需在对面的 Admin 上指定它们。

    使用内联:

    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',)
    

    (在 Django 3.0.3 上测试)

    【讨论】:

      【解决方案2】:

      您没有为此声明提供参考:

      Django 文档(可能是错误的)说“你必须告诉 Django 的管理员不要显示这个小部件”。

      所以我只能参考current (1.10) documentation for Django。它目前说的是ManyToMany fields in the admin

      Django 为定义关系的模型上的多对多字段显示一个管理小部件(在本例中为Group)。如果你想使用内联模型来表示多对多关系,你必须告诉 Django 的管理员显示这个小部件 - 否则你最终会在你的管理页面上有两个小部件用于管理关系。

      所以,针对您的正确陈述:

      但由于它是一种对称关系,因此没有任何逻辑理由不能从 Person 或 Group(但不能同时从两者)管理它而无需更改数据库架构。

      原因是必须在某处定义多对多关系;您已选择在 Group 模型上定义它,以便确定默认的管理员行为。如果您想移动它,那么您需要进行数据库迁移才能做到这一点。

      另一方面,如果您希望这种记录在案的行为有所不同而不改变您对它的使用 - 您似乎没有提出适合 StackOverflow 的问题。最好通过the project's bug tracker 报告程序的错误,要求更改软件的行为。

      【讨论】:

      • 当然,我可以重新定义架构并迁移数据库,正如原始问题中所承认的那样。管理界面没有内在的技术原因允许从一端而不是另一端管理对称关系 - 一个 应该 能够管理成员所属的组或成员列表组的,而不管关系是在哪里定义的。您只是重申了记录在案的限制,并未提供其存在的理由。
      • Dave 正在使用直通模型来获取内联,当然他使用哪个方向都没有关系。
      • 而且他还问了两次正确的 SO 问题! (这就是投反对票的原因)“但是如果管理员只想从组页面管理成员怎么办?” “所以在不改变模型的情况下,是否可以从个人页面而不是组页面中排除成员资格小部件?”编辑:实际上 3 次 - “Django Admin 可以从组页面管理组成员资格并将其从人员页面中排除吗?”
      猜你喜欢
      • 2015-08-28
      • 2016-02-23
      • 1970-01-01
      • 2012-10-20
      • 2016-03-21
      • 1970-01-01
      • 1970-01-01
      • 2010-11-16
      • 2011-08-28
      相关资源
      最近更新 更多