如何在Django Admin中按外键值筛选多对多字段选项
实现Django Admin中Exam关联Subject后过滤Question选项
要实现这个需求,你可以通过自定义ModelForm和Admin类,结合JavaScript实时联动来完成,以下是具体步骤:
步骤1:编写自定义ModelForm
在应用的admin.py中定义针对Exam的表单,初始化时根据已有实例的Subject过滤Question选项:
from django import forms from .models import Exam, Question class ExamAdminForm(forms.ModelForm): class Meta: model = Exam fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 编辑已有Exam时,过滤出对应Subject的Question if self.instance.pk: self.fields['questions'].queryset = Question.objects.filter(subject=self.instance.subject) # 新建时默认隐藏所有选项,直到选择Subject else: self.fields['questions'].queryset = Question.objects.none()
步骤2:自定义ExamAdmin并关联表单
注册Exam时使用自定义表单,同时添加模板注入JavaScript实现实时联动:
from django.contrib import admin from django.urls import reverse from django.utils.safestring import mark_safe class ExamAdmin(admin.ModelAdmin): form = ExamAdminForm # 指定自定义模板路径 change_form_template = 'admin/exam_change_form.html' def changeform_view(self, request, object_id=None, form_url='', extra_context=None): extra_context = extra_context or {} # 传递Question列表页的URL,用于动态获取过滤后的选项 extra_context['question_list_url'] = reverse('admin:你的应用名_question_changelist') return super().changeform_view(request, object_id, form_url, extra_context) admin.site.register(Exam, ExamAdmin)
步骤3:创建自定义模板实现实时联动
在应用目录下创建templates/admin/exam_change_form.html,添加JavaScript监听Subject选择事件:
{% extends "admin/change_form.html" %} {% block extrajs %} {{ block.super }} <script type="text/javascript"> (function($) { $(document).ready(function() { // 监听Subject下拉框的变化 $('#id_subject').on('change', function() { const subjectId = $(this).val(); const questionSelect = $('#id_questions'); if (!subjectId) { questionSelect.empty(); return; } // 请求对应Subject的Question列表 $.get("{{ question_list_url }}", {subject__id: subjectId}, function(data) { const options = $(data).find('#result_list tbody tr').map(function() { const id = $(this).find('td:first input').val(); const text = $(this).find('td:nth-child(2)').text().trim(); return $('<option>').val(id).text(text); }); questionSelect.empty().append(options); }); }); // 页面加载时如果已有Subject,触发一次选择事件 $('#id_subject').trigger('change'); }); })(django.jQuery); </script> {% endblock %}
简化方案(仅编辑时生效)
如果不需要实时联动,仅要求编辑已有Exam时显示对应Question,可以直接重写formfield_for_manytomany方法:
class ExamAdmin(admin.ModelAdmin): def formfield_for_manytomany(self, db_field, request, **kwargs): if db_field.name == 'questions': exam_id = request.resolver_match.kwargs.get('object_id') if exam_id: exam = Exam.objects.get(pk=exam_id) kwargs['queryset'] = Question.objects.filter(subject=exam.subject) else: kwargs['queryset'] = Question.objects.none() return super().formfield_for_manytomany(db_field, request, **kwargs) admin.site.register(Exam, ExamAdmin)
这个方案的局限是:新建Exam时必须先保存Subject,再进入编辑页面才能看到对应Question选项。
内容的提问来源于stack exchange,提问作者Yogesh Bhagat
相关产品推荐
相关产品推荐

