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
相关产品推荐
相关产品推荐

