使用django admin进行一对多内联选择

时间:2011-05-17 16:26:40

标签: python django django-models django-admin

我有一个标准的多对一关系设置。有很多字段,但出于我们的目的,相关模型是:

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

我创建了一个管理员,效果很好。在我编辑学生时,它甚至可以自动设置班级。但是,当我去创建/编辑一个类时,我得到的只是名称的输入框。

有没有办法添加一个框/字段,可以在Class管理页面中将学生添加为Class的成员?我可以在线创建一个表单,但那就是创建新的学生。我已经创建了所有学生,我只是在寻找一种快速方法,将多个现有学生添加到不同的班级。

7 个答案:

答案 0 :(得分:37)

有!您想要InlineModelAdmin (see InlineModelAdmin documentation here)

示例代码简要说明:

class StudentAdminInline(admin.TabularInline):
    model = Student

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

答案 1 :(得分:35)

这是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

答案 2 :(得分:0)

您还可以通过第二个模型来运行学生姓名,以使它们成为ForeignKey。例如,在原始代码后添加:

class AssignedStudents(models.Model):
    assigned_to = models.ForeignKey(Class, on_delete=models.CASCADE)
    student = models.ForeignKey(Student, on_delete=models.CASCADE)

然后像Luke Sneeringer所说的那样将内联添加到管理员中。您最终得到一个下拉列表,可以从中选择学生的名字。虽然,仍然可以选择创建新学生。

答案 3 :(得分:0)

这可能会帮助: 我使用了上述方法,但是以以下方式更改了方法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

答案 4 :(得分:0)

在 2021 年,这个问题没有直接的解决方案。

我所做的是将 ManyToManysymmetrical=False 结合使用。阅读更多Django official doc about symmetrical。只有在这种情况下,您才能在 django-admin

中选择多个实体
parent_questions = models.ManyToManyField('self', related_name='parent_questions',
                                         blank=True, symmetrical=False)

答案 5 :(得分:0)

Django 3.0.9 中对我有用的东西

models.py

from django import forms
from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=100)
    course = models.ForeignKey(Course, on_delete=models.CASCADE, null=True, blank=True)

    def __str__(self):
        return self.name


class CourseForm(forms.ModelForm):

    students = forms.ModelMultipleChoiceField(
        queryset=Student.objects.all(), required=False
    )
    name = forms.CharField(max_length=100)

    class Meta:
        model = Student
        fields = ["students", "name"]

    def __init__(self, *args, **kwargs):
        super(CourseForm, self).__init__(*args, **kwargs)
        if self.instance:
            self.fields["students"].initial = self.instance.student_set.all()

    def save_m2m(self):
        pass

    def save(self, *args, **kwargs):
        self.fields["students"].initial.update(course=None)
        course_instance = Course()
        course_instance.pk = self.instance.pk
        course_instance.name = self.instance.name
        course_instance.save()
        self.cleaned_data["students"].update(course=course_instance)
        return course_instance

admin.py

from django.contrib import admin
from .models import Student, Course

class StudentAdmin(admin.ModelAdmin):
    pass

class CourseAdmin(admin.ModelAdmin):
    form = CourseForm

admin.site.register(Student, StudentAdmin)
admin.site.register(Course, CourseAdmin)

答案 6 :(得分:-1)

如果要让学生独立于班级存在,然后能够将他们添加到班级中或从班级中删除,那么这将建立不同的关系:

  • 许多学生可以参加一个班级。
  • 一个学生可以参加许多课程

实际上,这是在描述多对多关系。只需交换

import anime from 'animejs'

const animateFunc = (params) => {
  const { node, on } = params
  const height = on ? 233 : 0
  return new Promise(resolve => {
    anime({
      targets: node,
      height,
      complete: () => { resolve() }
    }).play()
  })
}

对于

class Student(models.Model):
    class = models.ForeignKey(Class) ...

将立即在Django管理中提供所需的效果

Django Many-to-many