Django Admin 的一对多内联选择

77 投票
7 回答
43828 浏览
提问于 2025-04-16 17:49

我设置了一个标准的多对一关系。这里有很多字段,但我们现在关注的模型是:

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

class Student(models.Model):
    class = models.ForeignKey(Class)
    name = models.CharField(max_length=128)
    address = models.CharField(max_length=128)
    # ...etc

我创建了一个管理员界面,运行得很好。当我编辑学生时,它甚至可以让我自动选择班级。不过,当我去创建或编辑班级时,看到的只有一个输入框用来填写班级名称。

有没有办法在班级的管理员页面上添加一个框/字段,让我可以把学生添加为班级的成员?我可以做一个内联表单,但那是用来创建新学生的。我已经创建了所有的学生,现在只是想找个简单的方法,把多个已有的学生添加到不同的班级中。

7 个回答

4

可能这会对你有帮助:我使用了上面提到的方法,但对 savesave_m2m 这两个方法做了一些修改,具体如下:

from django import forms
from django.db import models
from django.contrib import admin

class Foo(models.Model):
     pass

class Bar(models.Model):
     foo = models.ForeignKey(Foo)

class FooForm(forms.ModelForm):
    class Meta:
        model = Foo

    bars = forms.ModelMultipleChoiceField(queryset=Bar.objects.all())

    def __init__(self, *args, **kwargs):
        super(FooForm, self).__init__(*args, **kwargs)
        if self.instance:
            self.fields['bars'].initial = self.instance.bar_set.all()

    def save_m2m(self):
        pass

    def save(self, *args, **kwargs):
        self.fields['bars'].initial.update(foo=None)
        foo_instance = Foo()
        foo_instance.pk = self.instance.pk
        # Copy all other fields.
        # ... #
        foo_instance.save()
        self.cleaned_data['bars'].update(foo=instance)
        return instance

class FooAdmin(admin.ModelAdmin):
    form = FooForm
54

有的!你需要用到 InlineModelAdmin(可以在这里查看 InlineModelAdmin 的文档)

下面是简短的示例代码:

class StudentAdminInline(admin.TabularInline):
    model = Student

class ClassAdmin(admin.ModelAdmin):
    inlines = (StudentAdminInline, )
admin.site.register(Class, ClassAdmin)
53

这是Luke Sneeringer建议的“自定义表单”解决方案。无论如何,我对Django没有现成的解决方案来处理这个(相当自然且可能很常见)问题感到惊讶。我是不是漏掉了什么?

from django import forms
from django.db import models
from django.contrib import admin

class Foo(models.Model):
    pass

class Bar(models.Model):
    foo = models.ForeignKey(Foo)

class FooForm(forms.ModelForm):
    class Meta:
        model = Foo

    bars = forms.ModelMultipleChoiceField(queryset=Bar.objects.all())

    def __init__(self, *args, **kwargs):
        super(FooForm, self).__init__(*args, **kwargs)
        if self.instance:
            self.fields['bars'].initial = self.instance.bar_set.all()

    def save(self, *args, **kwargs):
        # FIXME: 'commit' argument is not handled
        # TODO: Wrap reassignments into transaction
        # NOTE: Previously assigned Foos are silently reset
        instance = super(FooForm, self).save(commit=False)
        self.fields['bars'].initial.update(foo=None)
        self.cleaned_data['bars'].update(foo=instance)
        return instance

class FooAdmin(admin.ModelAdmin):
    form = FooForm

撰写回答