django admin - 选择反向外键关系(不创建,我想添加可用)

Posted

技术标签:

【中文标题】django admin - 选择反向外键关系(不创建,我想添加可用)【英文标题】:django admin - select reverse foreign key relationships (not create, I want to add available) 【发布时间】:2013-09-01 13:14:24 【问题描述】:

假设我有一个模型 School 和另一个模型 Student

class Student(models.Model):
   school = models.ForeignKey(School)
   name = models.CharField(max_length=100)

在管理员中点击学校时,会出现一个新页面,其中显示学校模型字段和值。

我还想在该页面本身中选择已经可用的学生列表。

Inlines 不同,它们将允许创建和编辑属于该学校的新记录(学生)。但我不希望这样,让我们​​假设已经有很多学生记录可用。我应该能够从该学校模型页面的管理员中选择它们。

【问题讨论】:

【参考方案1】:
class SchoolAdminForm(forms.ModelForm):
    students = forms.ModelMultipleChoiceField(
        queryset=Student.objects.all(),
        widget=FilteredSelectMultiple(verbose_name='students', is_stacked=False))

    class Meta:
        model = School
        fields = ['your_school_fields_go_here']

    def __init__(self, *args, **kwargs):
        super(SchoolAdminForm, self).__init__(*args, **kwargs)
        if self.instance:
            # fill initial related values
            self.fields['students'].initial = self.instance.student_set.all()

class SchoolAdmin(admin.ModelAdmin):
   form = SchoolAdminForm

   def save_model(self, request, obj, form, change):
       super().save_model(request, obj, form, change)
       original_students = set(obj.student_set.values_list("id", flat=True))
       current_students = set(map(lambda x: x.id, form.cleaned_data['students']))
       if original_students != current_students:
           add_to_school = current_students - original_students
           Student.objects.filter(id__in=add_to_school).update(school_id=obj.id)
           remove_from_school = original_students - current_students
           Student.objects.filter(id__in=remove_from_school).update(school_id=None)

【讨论】:

【参考方案2】:

我按照菲利克斯的建议做了。唯一的问题是它给出了这样的错误:

ValueError: Unsaved model instance <School: disscount_None> cannot be used in an ORM query.

我发现的原因是在保存当前 School 模型之前,您无法更改学生对象中的外键。因为还没有任何东西可以添加到 fk 属性中!所以使用 felix 解决方案,但在 save_model 函数中使用“for”之前的 obj.save()

   def save_model(self, request, obj, form, change):
       original_students = obj.student_set.all()
       new_students = form.cleaned_data['students']
       remove_qs = original_students.exclude(id__in=new_students.values('id'))
       add_qs = new_students.exclude(id__in=original_students.values('id'))


       obj.save()   

       for item in remove_qs:
               obj.student_set.remove(item)
       for item in add_qs:
               obj.student_set.add(item)
           

【讨论】:

【参考方案3】:

您可以禁用内联“添加”权限。见InlineModelAdmin

【讨论】:

【参考方案4】:

您的意思是对于给定的School 实例,您希望能够获得与该学校相关的所有学生的列表吗?

在这种情况下,您使用您指定的 ForeignKey 关系的 related_name 属性。你还没有定义你做的related_name

school = models.ForeignKey(School)

这很好,它只是使用默认的相关名称,即子类(学生)的名称,后跟_set

所以对于您的学校实例:

school = School.objects.get(pk=1)
students = school.student_set.all()  # or .filter() or .exclude() etc

然后您可以将该学生查询集传递到您的模板中。

【讨论】:

我知道。我想在管理员中做到这一点。因此,必须有一个简单的小部件。

以上是关于django admin - 选择反向外键关系(不创建,我想添加可用)的主要内容,如果未能解决你的问题,请参考以下文章

Django Rest DRF:从反向关系访问外键引用

Django admin 查询集通过外键向后关系过滤

Django admin内联显示但外键有相反的关系

Django Admin 自定义外键选择框

Django 深度序列化 - 遵循反向外键约束

django admin外键字段选择后如何关联显示另一个字段?