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

Django-filter无法使用RawQuerySet的技术问题求助

解决方案:RawQuerySet兼容Django-filter问题

你遇到的核心问题是DjangoFilterBackend依赖标准QuerySet的接口(如.all()、.filter()),而RawQuerySet不支持这些方法。下面提供几种可行的解决思路:

方案1:手动拼接过滤条件到原生SQL

直接在get_queryset中提取请求的过滤参数,动态修改原生SQL的WHERE子句,生成带过滤逻辑的RawQuerySet。这种方式无需依赖DjangoFilterBackend,完全自己控制过滤逻辑,适合复杂原生SQL场景。

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin

class RegionAssetViewSet(ListModelMixin, GenericViewSet):
    serializer_class = AssetServerInfoSerializer

    def get_queryset(self):
        # 基础原生SQL
        base_sql = """
        SELECT
            asset.id,
            region.id as region_id,
            asset.ip,
            asset.os_type,
            asset.status,
            asset.device_type_id
        FROM
            region_info AS region
            JOIN computer_info AS computer ON region.id = computer.region_id
            JOIN computer_cabinet_info AS cabinet ON computer.id = cabinet.computer_id
            JOIN asset_location_info AS location ON cabinet.id = location.cabinet_id
            JOIN asset_server_info AS asset ON location.asset_id = COALESCE ( asset.parent_id, asset.id ) 
        WHERE
            asset.del_flag = 0
        """
        filters = []
        params = []
        query_params = self.request.query_params

        # 根据你的ServerFilter定义的字段,逐个处理过滤参数
        # 示例:假设过滤字段包括os_type、status、device_type_id、region_id
        if 'os_type' in query_params:
            filters.append("asset.os_type = %s")
            params.append(query_params['os_type'])
        if 'status' in query_params:
            filters.append("asset.status = %s")
            params.append(query_params['status'])
        if 'device_type_id' in query_params:
            filters.append("asset.device_type_id = %s")
            params.append(query_params['device_type_id'])
        if 'region_id' in query_params:
            filters.append("region.id = %s")
            params.append(query_params['region_id'])

        # 拼接过滤条件到SQL
        if filters:
            base_sql += " AND " + " AND ".join(filters)
        
        return RegionAssetListVirtual.objects.raw(base_sql, params)

方案2:将原生SQL转为标准QuerySet(Django 3.2+)

如果你的RegionAssetListVirtual模型字段和原生SQL返回的字段完全匹配,可以用QuerySet的from_raw_query方法将原生SQL转为标准QuerySet,这样就能直接使用DjangoFilterBackend。

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from django.db.models import QuerySet
from django_filters.rest_framework import DjangoFilterBackend

class RegionAssetViewSet(ListModelMixin, GenericViewSet):
    serializer_class = AssetServerInfoSerializer
    filter_backends = [DjangoFilterBackend]
    filter_class = ServerFilter

    def get_queryset(self):
        region_asset_sql = """
        SELECT
            asset.id,
            region.id as region_id,
            asset.ip,
            asset.os_type,
            asset.status,
            asset.device_type_id
        FROM
            region_info AS region
            JOIN computer_info AS computer ON region.id = computer.region_id
            JOIN computer_cabinet_info AS cabinet ON computer.id = cabinet.computer_id
            JOIN asset_location_info AS location ON cabinet.id = location.cabinet_id
            JOIN asset_server_info AS asset ON location.asset_id = COALESCE ( asset.parent_id, asset.id ) 
        WHERE
            asset.del_flag = 0
        """
        # 将原生SQL转为标准QuerySet
        queryset = QuerySet(model=RegionAssetListVirtual, using=self.db).from_raw_query(region_asset_sql)
        return queryset

方案3:自定义FilterBackend兼容RawQuerySet

如果不想大幅修改现有代码,可以自定义一个FilterBackend,手动处理RawQuerySet的过滤逻辑,保留DjangoFilterBackend的过滤类配置。

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from django_filters.rest_framework.backends import DjangoFilterBackend

class RawQuerySetFilterBackend(DjangoFilterBackend):
    def filter_queryset(self, request, queryset, view):
        # 仅处理RawQuerySet
        if hasattr(queryset, 'raw_query'):
            filter_class = self.get_filter_class(view, queryset)
            if filter_class:
                filter_instance = filter_class(request.query_params, queryset=queryset)
                filters = []
                params = []
                # 从过滤实例中提取已验证的过滤参数
                for field_name, value in filter_instance.form.cleaned_data.items():
                    if value is not None:
                        # 根据过滤字段对应SQL中的表/别名调整字段名
                        if field_name == 'region_id':
                            filters.append(f"region.id = %s")
                        else:
                            filters.append(f"asset.{field_name} = %s")
                        params.append(value)
                if filters:
                    # 拼接过滤条件到原SQL
                    original_sql = queryset.raw_query
                    original_sql += " AND " + " AND ".join(filters)
                    # 返回新的RawQuerySet
                    return queryset.model.objects.raw(original_sql, params)
        # 非RawQuerySet走默认逻辑
        return super().filter_queryset(request, queryset, view)

class RegionAssetViewSet(ListModelMixin, GenericViewSet):
    serializer_class = AssetServerInfoSerializer
    filter_backends = [RawQuerySetFilterBackend]
    filter_class = ServerFilter

    def get_queryset(self):
        return RegionAssetListVirtual.objects.raw(region_asset_sql)

内容的提问来源于stack exchange,提问作者陈俊汝

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 19:36:16