You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Django Admin表单中通过外键过滤关联模型的纯Django实现

纯Django实现Admin中关联字段过滤(无JavaScript)

首先需要修正你代码中的拼写错误(这些错误会导致功能异常):

  • 模型类subject改为Subject(符合Python类命名规范)
  • 模型Subject中的外键字段discipline关联的Discipline改为Disciplina(对应你的模型类名)
  • 模型Question中的suject字段改为subject,关联Subject类
  • Admin代码中的Discipline改为Disciplina,Assunto改为Subject,Questao改为Question
  • QuestionAdmin中search_fields里的gabarito改为answer(对应模型字段名)
  • Subject模型的related_name改为subjects(拼写正确)

实现步骤

1. 修改QuestionAdmin,重写字段过滤逻辑

通过重写formfield_for_foreignkey方法,根据当前请求中已选择的Disciplina过滤Subject选项:

from django.contrib import admin
from django.http import HttpResponseRedirect
from django.urls import reverse
from .models import Disciplina, Subject, Question

@admin.register(Disciplina)
class DisciplinaAdmin(admin.ModelAdmin):
    list_display = ('name', 'origem')
    search_fields = ('name',)
    ordering = ('name',)

@admin.register(Subject)
class SubjectAdmin(admin.ModelAdmin):
    list_display = ('name', 'discipline_name')
    search_fields = ('name',)
    ordering = ('name',)

    def discipline_name(self, obj):
        return obj.discipline.name
    discipline_name.short_description = 'Disciplina'

@admin.register(Question)
class QuestionAdmin(admin.ModelAdmin):
    list_display = ('discipline_name', 'subject_name', 'latex_text', 'answer', 'dificulty', 'image', 'link_image')
    search_fields = ('latex_text', 'answer')
    ordering = ('discipline', 'subject')
    fields = ('discipline', 'subject', 'latex_text', 'math_jax', 'answer', 'dificulty', 'image', 'link_image', 'link_bucket')

    def discipline_name(self, obj):
        return obj.discipline.name
    discipline_name.short_description = 'Disciplina'

    def subject_name(self, obj):
        return obj.subject.name
    subject_name.short_description = 'Assunto'

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == 'subject':
            # 从URL参数中获取已选择的discipline ID
            discipline_id = request.GET.get('discipline')
            if discipline_id:
                kwargs['queryset'] = Subject.objects.filter(discipline_id=discipline_id)
            else:
                # 未选择discipline时,默认隐藏所有subject选项
                kwargs['queryset'] = Subject.objects.none()
        return super().formfield_for_foreignkey(db_field, request, **kwargs)

    def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
        extra_context = extra_context or {}
        # 传递已选择的discipline到模板
        if not object_id and 'discipline' in request.GET:
            extra_context['selected_discipline'] = Disciplina.objects.get(id=request.GET['discipline'])
        return super().changeform_view(request, object_id, form_url, extra_context)

    def response_add(self, request, obj, post_url_continue=None):
        # 处理「过滤科目」按钮的提交请求,不保存对象仅刷新页面
        if '_filter-subjects' in request.POST:
            discipline_id = request.POST.get('discipline')
            if discipline_id:
                return self.redirect_to_add_form(request, {'discipline': discipline_id})
        return super().response_add(request, obj, post_url_continue)

    def redirect_to_add_form(self, request, params):
        # 构造带过滤参数的添加页面URL
        url = reverse('admin:your_app_name_question_add')  # 替换your_app_name为实际应用名
        query_string = '&'.join([f"{k}={v}" for k, v in params.items()])
        return HttpResponseRedirect(f"{url}?{query_string}")

2. 自定义Admin添加页面模板,添加过滤触发按钮

在你的应用目录下创建templates/admin/your_app_name/question/change_form.html(替换your_app_name为实际应用名),写入以下内容:

{% extends "admin/change_form.html" %}
{% block form_top %}
    {% if add and not selected_discipline %}
        <p>请先选择Disciplina,点击下方「过滤科目」按钮加载对应科目选项</p>
    {% elif add and selected_discipline %}
        <p>已选择Disciplina: {{ selected_discipline.name }},科目列表已过滤</p>
    {% endif %}
{% endblock %}

{% block submit_buttons_bottom %}
    {% if add %}
        <div class="submit-row">
            <input type="submit" value="过滤科目" name="_filter-subjects" />
        </div>
    {% endif %}
    {{ block.super }}
{% endblock %}

工作流程

  1. 进入Question添加页面,此时Subject下拉框为空,提示先选择Disciplina
  2. 选择目标Disciplina,点击「过滤科目」按钮
  3. 页面刷新,URL携带discipline参数,Subject下拉框仅显示该Disciplina下的关联数据
  4. 填写剩余字段后保存即可

注意事项

  • 无JavaScript情况下需手动触发过滤,修改Disciplina后需重新点击按钮更新Subject列表
  • 确保应用已添加到INSTALLED_APPS,且模板目录配置正确

内容的提问来源于stack exchange,提问作者ftrajano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 01:40:59