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

Django+Vue.js项目中medical_package筛选无返回结果问题求助

问题说明

当前网站配置了两个筛选输入框,分别用于员工筛选、医疗套餐筛选。其中员工维度筛选功能运行正常,但医疗套餐筛选始终无法返回有效结果。已在filters.py中声明medical_package__in过滤字段,执行筛选时仍无匹配结果返回,以下是前后端相关实现代码,需定位问题根因。

相关实现代码

后端 - filters.py

class StaffMedicalPackageFilter(FilterSet):
    staff__in = BaseInFilter(field_name="staff", lookup_expr='in')
    staff__username_in = BaseInFilter(field_name="staff__username", lookup_expr='in')
    department__in = BaseInFilter(field_name="staff__department", lookup_expr='in')
    medical_package__in = BaseInFilter(field_name="medical_package__id", lookup_expr='in')
    created_at__gte = DateTimeFilter(field_name="created_at", lookup_expr='gte')
    created_at__lt = DateTimeFilter(field_name="created_at", lookup_expr='lt')

    class Meta:
        model = StaffMedicalPackage
        fields = [
           'staff',
           'staff__in',
           'staff__username_in',
           'medical_package__in',
           'department__in',
           'created_at__gte',
           'created_at__lt',]

后端 - view.py

class StaffMedicalPackageList(generics.ListCreateAPIView):
    queryset = StaffMedicalPackage.objects.all()
    serializer_class = StaffMedicalPackageSerializer
    filterset_class = StaffMedicalPackageFilter
    permission_classes = [permissions.IsAuthenticated,]

@decorators.api_view(['OPTIONS'])
@decorators.permission_classes([permissions.IsAuthenticated,])
def staffmedicalpackage_filters(request):
    result =  {
        "actions": {
            "POST": {
                "staff__in": {
                    "type": "field",
                    "required": False,
                    "read_only": True,
                    "label": "Staff"
                },
                "medical_package__in": {
                    "type": "field",
                    "required": False,
                    "read_only": False,
                    "label": "Medical package",
                }
            }
        }
    }

    return response.Response(result)

后端 - serializers.py

class StaffMedicalPackageSerializer(serializers.ModelSerializer):
    staff_detail = StaffSerializer(read_only=True, source='staff')
    medical_package_detail = MedicalPackageSerializer(read_only=True, source='medical_package')
    current_year = serializers.ChoiceField(choices = YEARS, source = 'created_at', required=False)

    class Meta:
        model = StaffMedicalPackage
        fields = [
            "id",
            "created_at",
            "updated_at",
            "current_year",
            "medical_package",
            "medical_package_detail",
            "staff",
            "staff_detail",
            'general_balance',
            'dental_balance',
            'exceeded_amount',]

前端 - index.vue

methods: {

    async getFilters() {
        let tmp = await this.api.medical.optionsStaffMedicalPackageFilters();
        this.filters = tmp

        this.$set(this.filters.staff__in, "item_text", "name")
        this.$set(this.filters.staff__in, "item_value", "id")
        this.$set(this.filters.staff__in, "read_only", true);
        this.$set(this.filters.staff__in, "disabled", true);
        this.$set(this.filters.staff__in, "items", await this.api.training.listStaffs({}));
        this.$set(this.filters.staff__in, "read_only", false);
        this.$set(this.filters.staff__in, "disabled", false);

        this.$set(this.filters.medical_package__in, "item_text", "medical_package_name")
        this.$set(this.filters.medical_package__in, "item_value", "id")
        this.$set(this.filters.medical_package__in, "read_only", true);
        this.$set(this.filters.medical_package__in, "disabled", true);
        this.$set(this.filters.medical_package__in, "items", await this.api.medical.listMedicalPackage());

        this.$set(this.filters.medical_package__in, "read_only", false);
        this.$set(this.filters.medical_package__in, "disabled", false);

    },
    async loadItems() {
      let params = {}

      for (const property in this.filters) {
        if (this.filters[property].hasOwnProperty('value')) {
          params[property] = this.filters[property].value
        }
      }

      this.isTableLoading = true;
      this.tableData = await this.api.medical.listStaffMedicalPackage(params);
      this.isTableLoading = false;
    },
    getTableHeader() {
      this.tableHeaders = [
        { text: 'Staff', value: 'staff_detail' },
        { text: 'Medical Package', value: 'medical_package_detail' },
        { text: 'General Balance (RM)', value: 'general_balance' },
        { text: 'Dental Balance (RM)', value: 'dental_balance' },
        { text: 'Exceeded Amount (RM)', value: 'exceeded_amount' },
        { text: 'Created At', value: 'created_at' },
        { text: 'Updated At', value: 'updated_at' },
        { text: 'Actions', value: 'actions', sortable: false },
      ];
    }
问题根因与修复方案

排查结论

  • 后端过滤器定义无语法错误:medical_package__in 绑定field_name="medical_package__id"、lookup_expr='in'的写法符合django-filter规范,和正常工作的staff__in定义逻辑一致。
  • 接口配置无问题:view中正确注册了过滤类,OPTIONS接口返回的筛选字段名和后端定义完全匹配。
  • 序列化器配置无问题:正确暴露了medical_package外键字段,不存在字段屏蔽导致过滤失效的情况。
  • 核心问题出在前端参数格式:BaseInFilter 默认要求传入逗号分隔的字符串格式参数,当前前端直接传递数组类型的选中值时,axios会默认将数组序列化为medical_package__in[]=1&medical_package__in[]=2格式,django-filter无法识别该格式参数,相当于过滤条件未生效,自然匹配不到数据。员工筛选能正常运行是因为该筛选默认是单选,传递单个ID值不属于多值in查询场景,没有触发格式问题。

修复方案

前端修复(优先选择)

修改loadItems方法的参数拼接逻辑,对__in结尾的筛选参数,将数组值转换为逗号分隔的字符串再传递:

async loadItems() {
  let params = {}

  for (const property in this.filters) {
    if (this.filters[property].hasOwnProperty('value') && this.filters[property].value !== null && this.filters[property].value !== '') {
      const val = this.filters[property].value
      // 处理in查询的数组值,转换为逗号分隔字符串
      if (property.endsWith('__in') && Array.isArray(val)) {
        params[property] = val.join(',')
      } else {
        params[property] = val
      }
    }
  }

  this.isTableLoading = true;
  this.tableData = await this.api.medical.listStaffMedicalPackage(params);
  this.isTableLoading = false;
}

后端兼容方案(可选)

如果需要后端兼容数组格式传参,可以自定义支持数组解析的过滤器替换原有BaseInFilter:

class ArrayCompatibleInFilter(BaseInFilter):
    def filter(self, qs, value):
        if value and isinstance(value, list):
            value = ','.join([str(v) for v in value])
        return super().filter(qs, value)

# 替换filters.py中所有BaseInFilter即可
class StaffMedicalPackageFilter(FilterSet):
    staff__in = ArrayCompatibleInFilter(field_name="staff", lookup_expr='in')
    staff__username_in = ArrayCompatibleInFilter(field_name="staff__username", lookup_expr='in')
    department__in = ArrayCompatibleInFilter(field_name="staff__department", lookup_expr='in')
    medical_package__in = ArrayCompatibleInFilter(field_name="medical_package__id", lookup_expr='in')
    # 其余字段保持原有定义不变

内容的提问来源于stack exchange,提问作者Gavin Teo Juen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 11:15:30