Django admin:通过不相邻的ManyToMany关系使用内联

时间:2017-11-20 01:59:53

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

考虑以下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。

谢谢!

1 个答案:

答案 0 :(得分:0)

我通过略微修改我的要求解决了这个问题。我没有要求PhonePerson之间的关系,而是添加了另一种多对一关系:PhoneGroup之间。对于我的特殊情况,它实际上更好用这种方式;并且Group应该在删除时与相关Person和相关Phone级联。

问题中显示的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表单窗口小部件仅显示同一Person中的Groupadmin.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)

{{1}}