【问题标题】:Django admin: using inlines through a non-adjacent ManyToMany relationshipDjango admin:通过非相邻的多对多关系使用内联
【发布时间】:2017-12-03 06:25:26
【问题描述】:

考虑以下models.py,其中一个组包含多个人,每个人都有零个或多个电话号码。在这种特殊情况下,共享组的人员通常会共享至少一个电话号码,因此使用了多对多关系。

class Group(models.Model):
    name = models.CharField(max_length=30)

class Person(models.Model):
    group = models.ForeignKey(Group)
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

class Phone(models.Model):
    persons = models.ManyToManyField(Person)
    number = models.CharField(max_length=30)

我想在 Django 管理中以单个视图显示这些模型,如下所示。

class PersonInline(admin.StackedInline):
    model = Person

class PhoneInline(admin.StackedInline):
    model = Phone # also tried: Phone.persons.through

@admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
    inlines = [PersonInline, PhoneInline]

但是,Group 和 Phone 之间没有外键,因此这会引发 SystemCheckError(以下之一):
<class 'myapp.admin.PhoneInline'>: (admin.E202) 'myapp.Phone' has no ForeignKey to 'myapp.Group'.
<class 'myapp.admin.PhoneInline'>: (admin.E202) 'myapp.Phone_persons' has no ForeignKey to 'myapp.Group'.

是否可以通过 Person 模型完成这项工作?内联电话的目标是显示组中所有人员的电话号码记录(奖励:添加新电话时,Person SelectMultiple 小部件只需要显示组中的其他人员)。我宁愿避免修改任何模板。如有必要,可以集成第三方应用程序。我可以使用 Django 1.10 或 1.11。

谢谢!

【问题讨论】:

    标签: python django django-models django-forms django-admin


    【解决方案1】:

    我通过稍微修改我的要求解决了这个问题。我不仅需要PhonePerson 之间的关系,还添加了另一个多对一关系:PhoneGroup 之间的关系。对于我的特殊情况,这种方式实际上效果更好;并且 Group 应该在删除时与相关的 Persons 和相关的 Phones 级联。

    问题中显示的admin.py 没有变化。 models.pyPhone 类中多了一行,一个 ForeignKey 字段:

    class Phone(models.Model):
        group = models.ForeignKey(Group)
        persons = models.ManyToManyField(Person)
        number = models.CharField(max_length=30)
    

    上面的“奖励”要求 Person 内联中的 ManyToManyField 表单小部件仅显示位于同一 Group 中的那些 Persons。为此,可以给admin.py添加两个函数:

    class PersonInline(admin.StackedInline):
        model = Person
    
    class PhoneInline(admin.StackedInline):
        model = Phone
    
        def formfield_for_manytomany(self, db_field, request=None, **kwargs):
            field = super(PhoneInline,
                          self).formfield_for_manytomany(db_field, request,
                                                         **kwargs)
            if db_field.name == 'persons':
                if request._obj_ is None:
                    field.queryset = field.queryset.none()
                else:
                    qs = Person.objects.filter(group=request._obj_.id)
                    field.queryset = qs
                    field.initial = qs
            return field
    
    @admin.register(Group)
    class GroupAdmin(admin.ModelAdmin):
        inlines = [PersonInline, PhoneInline]
    
        def get_form(self, request, obj=None, **kwargs):
            # Save obj reference for future processing in Phone inline
            request._obj_ = obj
            return super(GroupAdmin, self).get_form(request, obj, **kwargs)
    

    【讨论】:

      猜你喜欢
      • 2011-08-07
      • 2012-10-20
      • 2016-04-18
      • 2015-02-15
      • 2011-02-23
      • 2011-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多