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

Django ModelForm实现科室联动更新医生字段方案咨询

科室选择联动医生列表的实现方案

核心逻辑划分

  • 前端: 用原生JavaScript监听科室选择事件,异步请求后端获取对应医生数据并更新下拉框(必须用JS,因为是无刷新动态更新)
  • 后端: 联动逻辑写在视图中(提供接口返回过滤后的医生数据)
  • ModelForm: 仅负责渲染基础表单字段,联动逻辑完全不放在Form里

1. 匹配场景的模型结构(参考常见医院项目)

# models.py
from django.db import models

class Speciality(models.Model):
    name = models.CharField(max_length=100, unique=True)

    def __str__(self):
        return self.name

class Doctor(models.Model):
    name = models.CharField(max_length=100)
    speciality = models.ForeignKey(Speciality, on_delete=models.CASCADE, related_name='doctors')

    def __str__(self):
        return self.name

# 示例预约模型(或其他需要科室+医生关联的业务模型)
class Appointment(models.Model):
    patient_name = models.CharField(max_length=100)
    speciality = models.ForeignKey(Speciality, on_delete=models.CASCADE)
    doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE)
    appointment_date = models.DateTimeField()

2. 调整ModelForm配置

# forms.py
from django import forms
from .models import Appointment, Doctor

class AppointmentForm(forms.ModelForm):
    # 初始化时设置空的医生查询集,避免加载全部医生数据
    doctor = forms.ModelChoiceField(queryset=Doctor.objects.none(), required=True, label="医生")

    class Meta:
        model = Appointment
        fields = ['patient_name', 'speciality', 'doctor', 'appointment_date']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 编辑场景:如果已有实例,加载对应科室的医生
        if self.instance.pk:
            self.fields['doctor'].queryset = self.instance.speciality.doctors.order_by('name')
        # POST请求场景:如果提交时带了科室ID,加载对应医生
        elif 'speciality' in self.data:
            try:
                speciality_id = int(self.data.get('speciality'))
                self.fields['doctor'].queryset = Doctor.objects.filter(speciality_id=speciality_id).order_by('name')
            except (ValueError, TypeError):
                pass

3. 后端添加医生数据接口

# views.py
from django.http import JsonResponse
from django.views import View
from .models import Doctor

class DoctorBySpecialityView(View):
    def get(self, request):
        speciality_id = request.GET.get('speciality_id')
        # 验证参数合法性
        if not speciality_id or not speciality_id.isdigit():
            return JsonResponse([], safe=False)
        # 返回对应科室的医生ID和名称
        doctors = Doctor.objects.filter(speciality_id=speciality_id).values('id', 'name')
        return JsonResponse(list(doctors), safe=False)

# 在urls.py中添加路由
# from .views import DoctorBySpecialityView
# path('api/doctors/', DoctorBySpecialityView.as_view(), name='doctor-by-speciality'),

4. 前端模板添加JS联动逻辑

<!-- appointment_form.html -->
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">提交预约</button>
</form>

<script>
// 获取Django自动生成的表单元素ID(注意字段名要和Form定义一致)
const specialitySelect = document.getElementById('id_speciality');
const doctorSelect = document.getElementById('id_doctor');

// 监听科室选择变化
specialitySelect.addEventListener('change', function() {
    const specialityId = this.value;

    // 未选择科室时清空医生下拉
    if (!specialityId) {
        doctorSelect.innerHTML = '<option value="">请先选择科室</option>';
        return;
    }

    // 发送异步请求获取医生数据
    fetch(`/api/doctors/?speciality_id=${specialityId}`)
        .then(response => {
            if (!response.ok) throw new Error('请求失败');
            return response.json();
        })
        .then(data => {
            // 清空下拉框并添加新选项
            doctorSelect.innerHTML = '<option value="">请选择医生</option>';
            data.forEach(doctor => {
                const option = document.createElement('option');
                option.value = doctor.id;
                option.textContent = doctor.name;
                doctorSelect.appendChild(option);
            });
        })
        .catch(() => {
            doctorSelect.innerHTML = '<option value="">加载失败,请重试</option>';
        });
});

// 页面加载时,若已有选中科室(编辑场景),自动触发联动
window.addEventListener('load', () => {
    if (specialitySelect.value) {
        specialitySelect.dispatchEvent(new Event('change'));
    }
});
</script>

关键注意事项

  • 确保id_speciality和id_doctor与你的Form字段ID一致(Django默认用id_<字段名>命名规则)
  • 开发时打开浏览器控制台(F12)查看错误信息,快速定位问题
  • 若需要兼容旧浏览器,可以把fetch换成XMLHttpRequest,现代浏览器均支持fetch
  • 后端接口可根据项目需求添加权限验证(比如仅登录用户访问)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 10:10:53