DRF的Filter组件
如果某个API需要传递一些条件进行搜索,其实就在是URL后面通过GET传参即可,例如:
/api/users?age=19&category=12
在drf中filter组件可以支持条件搜索。
1. 自定义filter
# models.py
from django.db import modelsclass Role(models.Model):""" 角色表 """title = models.CharField(verbose_name='名称', max_length=32)class Department(models.Model):""" 部门表 """title = models.CharField(verbose_name='名称', max_length=32)class UserInfo(models.Model):username = models.CharField(verbose_name='用户名', max_length=32)age = models.CharField(verbose_name='年龄', max_length=32)level_choice = ((1, 'VIP'), (2, 'SVIP'), (3, 'PARTNER'))level = models.SmallIntegerField(verbose_name='级别', choices=level_choice)email = models.CharField(verbose_name='邮箱', max_length=32)# 创建外键depart = models.ForeignKey(verbose_name="部门", to="Department", on_delete=models.CASCADE)# 多对多roles = models.ManyToManyField(verbose_name="角色", to="Role")
# views.py
from rest_framework import serializers
from rest_framework.filters import BaseFilterBackend
from rest_framework.viewsets import ModelViewSetfrom api import models# Create your views here.
class UserSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display", read_only=True)extra = serializers.SerializerMethodField(read_only=True)class Meta:model = models.UserInfofields = ['username', 'age', 'email', "level_text", 'extra']def get_extra(self, obj):return '我是多余的'# 自定义Filter
class Filter1(BaseFilterBackend):def filter_queryset(self, request, queryset, view):age = request.GET.get('age') # 可以使用request.query_paramsif not age:return querysetreturn queryset.filter(age=age)class Filter2(BaseFilterBackend):def filter_queryset(self, request, queryset, view):id = request.query_params.get('id')if not id:return querysetreturn queryset.filter(id=id)class UserView(ModelViewSet):filter_backends = [Filter1, Filter2] # 加入需要传递的Filterqueryset = models.UserInfo.objects.all() # GenericAPIView这个类提供的变量serializer_class = UserSerializer
返回值:
源码流程
2. 第三方filter(常用)
在drf开发中有一个常用的第三方过滤器:DjangoFilterBackend。
pip install django-filter
注册app:
INSTALLED_APPS = [...'django_filters',...
]
示例1: 简单
视图配置和应用:
# views.py
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend
from app01 import modelsclass UserModelSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display",read_only=True)extra = serializers.SerializerMethodField(read_only=True)class Meta:model = models.UserInfofields = ["username", "age", "email", "level_text", "extra"]def get_extra(self, obj):return 666class UserView(ModelViewSet):filter_backends = [DjangoFilterBackend, ]filterset_fields = ["id", "age", "email"]queryset = models.UserInfo.objects.all()serializer_class = UserModelSerializerdef perform_create(self, serializer):""" 序列化:对请求的数据校验成功后,执行保存。"""serializer.save(depart_id=1, password="123")
示例2: 复杂
视图配置和应用(示例3):
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend, OrderingFilter
from django_filters import FilterSet, filters
from app01 import modelsclass UserModelSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display",read_only=True)depart_title = serializers.CharField(source="depart.title",read_only=True)extra = serializers.SerializerMethodField(read_only=True)class Meta:model = models.UserInfofields = ["id", "username", "age", "email", "level_text", "extra", "depart_title"]def get_extra(self, obj):return 666class MyFilterSet(FilterSet):# /api/users/?min_id=2 -> id>=2min_id = filters.NumberFilter(field_name='id', lookup_expr='gte')# /api/users/?name=wupeiqi -> not ( username=wupeiqi )name = filters.CharFilter(field_name="username", lookup_expr="exact", exclude=True)# /api/users/?depart=xx -> depart__title like %xx%depart = filters.CharFilter(field_name="depart__title", lookup_expr="contains")# /api/users/?token=true -> "token" IS NULL# /api/users/?token=false -> "token" IS NOT NULLtoken = filters.BooleanFilter(field_name="token", lookup_expr="isnull")# /api/users/?email=xx -> email like xx%email = filters.CharFilter(field_name="email", lookup_expr="startswith")# /api/users/?level=2&level=1 -> "level" = 1 OR "level" = 2(必须的是存在的数据,否则报错-->内部有校验机制)# level = filters.AllValuesMultipleFilter(field_name="level", lookup_expr="exact")level = filters.MultipleChoiceFilter(field_name="level", lookup_expr="exact", choices=models.UserInfo.level_choices)# /api/users/?age=18,20 -> age in [18,20]age = filters.BaseInFilter(field_name='age', lookup_expr="in")# /api/users/?range_id_max=10&range_id_min=1 -> id BETWEEN 1 AND 10range_id = filters.NumericRangeFilter(field_name='id', lookup_expr='range')# /api/users/?ordering=id -> order by id asc# /api/users/?ordering=-id -> order by id desc# /api/users/?ordering=age -> order by age asc# /api/users/?ordering=-age -> order by age descordering = filters.OrderingFilter(fields=["id", "age"])# /api/users/?size=1 -> limit 1(自定义搜索)size = filters.CharFilter(method='filter_size', distinct=False, required=False)class Meta:model = models.UserInfofields = ["id", "min_id", "name", "depart", "email", "level", "age", 'range_id', "size", "ordering"]def filter_size(self, queryset, name, value):int_value = int(value)return queryset[0:int_value]class UserView(ModelViewSet):filter_backends = [DjangoFilterBackend, ]filterset_class = MyFilterSetqueryset = models.UserInfo.objects.all()serializer_class = UserModelSerializerdef perform_create(self, serializer):""" 序列化:对请求的数据校验成功后,执行保存。"""serializer.save(depart_id=1, password="123")
补充
lookup_expr
有很多常见选择:
'exact': _(''),
'iexact': _(''),'contains': _('contains'),
'icontains': _('contains'),
'startswith': _('starts with'),
'istartswith': _('starts with'),
'endswith': _('ends with'),
'iendswith': _('ends with'),'gt': _('is greater than'),
'gte': _('is greater than or equal to'),
'lt': _('is less than'),
'lte': _('is less than or equal to'),'in': _('is in'),
'range': _('is in range'),
'isnull': _(''),'regex': _('matches regex'),
'iregex': _('matches regex'),
全局配置和应用:
# settings.py 全局配置REST_FRAMEWORK = {'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend',]
}
3. 内置filter
drf源码中内置了2个filter,分别是:
-
OrderingFilter,支持排序。
from rest_framework import serializers from rest_framework.viewsets import ModelViewSet from app01 import models from rest_framework.filters import OrderingFilterclass UserModelSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display",read_only=True)depart_title = serializers.CharField(source="depart.title",read_only=True)extra = serializers.SerializerMethodField(read_only=True)class Meta:model = models.UserInfofields = ["id", "username", "age", "email", "level_text", "extra", "depart_title"]def get_extra(self, obj):return 666class UserView(ModelViewSet):filter_backends = [OrderingFilter, ]# ?order=id# ?order=-id# ?order=ageordering_fields = ["id", "age"]queryset = models.UserInfo.objects.all()serializer_class = UserModelSerializerdef perform_create(self, serializer):""" 序列化:对请求的数据校验成功后,执行保存。"""serializer.save(depart_id=1, password="123")
-
SearchFilter,支持模糊搜索。
from rest_framework import serializers from rest_framework.viewsets import ModelViewSet from app01 import models from rest_framework.filters import SearchFilterclass UserModelSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display",read_only=True)depart_title = serializers.CharField(source="depart.title",read_only=True)extra = serializers.SerializerMethodField(read_only=True)class Meta:model = models.UserInfofields = ["id", "username", "age", "email", "level_text", "extra", "depart_title"]def get_extra(self, obj):return 666class UserView(ModelViewSet):filter_backends = [SearchFilter, ]search_fields = ["id", "username", "age"]queryset = models.UserInfo.objects.all()serializer_class = UserModelSerializerdef perform_create(self, serializer):""" 序列化:对请求的数据校验成功后,执行保存。"""serializer.save(depart_id=1, password="123")
"app01_userinfo"."id" LIKE %18% ESCAPE '\' OR "app01_userinfo"."username" LIKE %18% ESCAPE '\' OR "app01_userinfo"."age" LIKE %18% ESCAPE '\'