一、简介
REST框架的通用列表视图的默认行为是返回模型管理器的整个查询集。
筛选子类的任何视图的查询集的最简单方法是覆盖该GenericAPIView 的 .get_queryset() 方法。
基于 URL 传递的参数过滤
- URL
考虑如下 URL 配置条目:
path('servers-base-url-args/<manufacturer>', views.ServersListView.as_view()),
- URL 请求
http://www.qfsite.com/api/servers-base-url-args/del/
- 视图
URL 传递的参数会在 self.kwargs 或者 self.args 中
class ServersListView(generics.ListAPIView):
serializer_class = ServersSerializer
def get_queryset(self):
"""
默认返回所有的服务器,这里使用了过滤,
只返回某一个厂商的。
"""
manufacturer = self.kwargs['manufacturer']
return Servers.objects.filter(manufacturer=manufacturer)
基于 GET 方法中的参数过滤
- URL
考虑如下 URL 配置条目:
path('servers-base-get-args/', ServersListView.as_view()),
- URL 请求
http://www.qfsite.com/api/servers-base-get-args/?manufacturer=del
- 视图
GET 方法传参是在self.request.query_params
中,这是一个字典。
class ServersListView(generics.ListAPIView):
serializer_class = ServersSerializer
def get_queryset(self):
"""
默认返回所有的服务器,这里使用了过滤,
只返回某一个厂商的。
"""
queryset = Servers.objects.all()
manufacturer = self.request.query_params.get('manufacturer', None)
if manufacturer:
queryset = queryset.filter(manufacturer=manufacturer)
return queryset
通用过滤器(重点掌握)
REST框架还包括对通用过滤后端的支持,允许您轻松构建复杂的搜索和过滤器。
通用过滤器还可以在可浏览的API和管理API中将自身呈现为HTML控件。
配置过滤器后端 django-filters
DjangoFilterBackend
该 django-filter 库包含一个 DjangoFilterBackend 类,该类支持 REST 框架的高度可定制的字段过滤。
要使用 DjangoFilterBackend,请先安装 django-filter。然后添加 django_filters 到 Django的 INSTALLED_APPS
pip3 install django-filter
- 注册 APP
在 settings.py 中设置
INSTALLED_APPS = [
...
'django_filters',
]
注意: 中间的是下划线 _
2.1 为全局设置
您现在应该将过滤器后端添加到您的设置:
在 settings.py 中设置
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}
2.2 或者为具体某个视图设置
或者将过滤器后端添加到单个View或ViewSet。
view 或者 ViewSet 必须是基于 GenericAPIView 的类视图
from django_filters.rest_framework import DjangoFilterBackend
class UserListView(generics.ListAPIView):
...
filter_backends = [DjangoFilterBackend]
- 如何使用
3.1 视图
过滤字段的值必须是 等于的关系
只需要在视图或视图集上设置 filterset_fields 属性,并列出要过滤的字段集
from django_filters.rest_framework import DjangoFilterBackend
class ServersListView(generics.ListAPIView):
queryset = Servers.objects.all()
serializer_class = ServersSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['hostname', 'manufacturer']
3.2 URL 配置
urlpatterns = [
path("servers-filter/", views.ServersFilterView.as_view()),
]
3.3 URL 请求
这将接受下面的请求:
http://www.qfedu.com/api/servers-filter/?hostname=db.server.yy.com&manufacturer=
3.4 效果图
填写过滤条件
过滤结果:
可以发现 django-filter
默认的过滤器支持的只是精确匹配。
假如希望进行模糊匹配,比如希望查找到所有包含 db.server 的主机名等。
这样的需求可以采用自定义过滤器的的方式。
自定义过滤类
利用 django-filter
自定义过滤类,对于更高级的过滤要求,您可以自己编写一个过滤器,这个过滤器很想 Django 中的 From 类 ,之后在指定视图中使用 filterset_class=自定义类名
来使用这个类。
1. 编写自定义过滤器的类
这个过滤器的目标是: 查找到创建时间在某个范围的服务器。
from django_filters import rest_framework as filters
from users.models import Server
class ServersFilter(filters.FilterSet):
"""服务器的过滤类"""
# 可以自定义筛选器属性名,如 create_at_min
create_at_min = filters.DateTimeFilter(
field_name='create_at', lookup_expr='gte',label="创建时间 大于等于")
create_at_max = filters.DateTimeFilter(
field_name='create_at', lookup_expr='lte', label="创建时间 小于等于")
class Meta:
model = Server
fields = ['create_at_min', 'create_at_max']
核心参数
field_name
: 筛选依据的模型字段的名称。如果未提供此参数,则会默认FilterSet类上的筛选器属性名称。字段名可以通过将相关部分与ORM查找分隔符(__
)连接来遍历关系。例如manufacturer__name
。lookup_eper
: 应在筛选器调用中执行的字段查找。默认为精确匹配。如果表达式部分由ORM查找分隔符(__
)连接,则lookup_expr
可以包含更多的查找转换规则。例如,通过日期时间的年部分year__gt
来过滤日期时间。
label 属性的值会显示到页面提示上。
- 在视图中使用
from django_filters.rest_framework import DjangoFilterBackend
from .filters import ServersFilter
class ServersFilterViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Server.objects.all()
serializer_class = ServerSerializer
filter_backends = [DjangoFilterBackend]
filterset_class = ServersFilter
- URL 配置
由于上面的视图是采用视图集(ViewSet)的方式,所以可以使用路由注册的方式。
router.register(r'servers-filter', views.ServersFilterViewSet)
-
效果图
-
支持字段值的模糊匹配
对主机名 hostname 进行模糊匹配,过滤器需进行如下配置
模糊匹配相当于 MySql 中的 like.
这里使用icontains
, i 是忽略大小写的。
class ServersFilter(filters.FilterSet):
"""服务器的过滤类"""
create_at = filters.DateFromToRangeFilter(label="创建时间范围搜索after-before")
hostname = filters.CharFilter(field_name='hostname', lookup_expr='icontains')
class Meta:
model = Server
fields = ['create_at', 'hostname']
注意: Model 中的字段是 models.DateTimeField (年-月-日 时:分:秒).
在这里仍然可以使用 filters.DateFromToRangeFilter (年-月-日) 的方式进行过滤。
关键字参数 method
一个可选参数,告诉筛选器如何处理查询集。它可以接受FilterSet上的方法名称或者任意位置编写的函数。方法或函数接收一个QuerySet queryset
、要筛选的模型字段的名称 name
以及要筛选的值 value
。它应该返回一个经过筛选的Queryset。
请注意,该值由Filter.field验证,因此不需要原始值转换和空值检查。
参考官方示例如下:
class F(FilterSet):
"""根据图书是否已出版筛选图书"""
published = BooleanFilter(field_name='published_on', method='filter_published')
def filter_published(self, queryset, name, value):
# 构造完整的查找表达式。
lookup = '__'.join([name, 'isnull'])
return queryset.filter(**{lookup: False})
# 或者, 对于 lookup 你可以使用硬编码。 比如,
# return queryset.filter(published_on__isnull=False)
class Meta:
model = Book
fields = ['published']
# 也可以在类作用域之外定义一个函数
def filter_not_empty(queryset, name, value):
lookup = '__'.join([name, 'isnull'])
return queryset.filter(**{lookup: False})
class F(FilterSet):
"""根据图书是否已出版筛选图书"""
published = BooleanFilter(field_name='published_on', method=filter_not_empty)
class Meta:
model = Book
fields = ['published']
更多
点我官方文档-过滤参考
支持跨表字段的匹配
如下示例中使用的 model:
class Servers(models.Model):
"""服务器"""
ENV = (
("0", "测试"),
("1", "生产"),
("2", "开发"),
("3", "预生产"),
)
project = models.ForeignKey(
"projects.Projects", related_name="servers", on_delete=models.CASCADE,
verbose_name="所属项目", help_text="服务器属于哪个项目")
env = models.CharField(max_length=1, choices= ENV,default="1", verbose_name="环境", help_text="哪个环境的服务器")
host_name = models.CharField(
max_length=128, unique=True, db_index=True, verbose_name="主机名", help_text="主机名")
class Meta:
"""元数据"""
verbose_name = "服务器表"
verbose_name_plural = verbose_name
class Projects(models.Model):
"""项目表"""
name = models.CharField(
max_length=30, verbose_name="项目名称", help_text="项目名称")
class Meta:
"""定义数据库中实际的表名称"""
verbose_name = "项目表"
verbose_name_plural = verbose_name
实现关系字段的过滤,可以使用下面的示例:
class ServersFilter(filters.FilterSet):
"""服务器的过滤类"""
project = filters.CharFilter(field_name='project__name', lookup_expr='icontains', label="所属项目")
class Meta:
model = Servers
fields = ["env", "local_ip", "project"]
field_name 的值 project__name
,表示了关系字段的查询。
fields 中 project
是过滤器的属性名称,也是前端发送请求时候需要传递的 GET 请求参数名称。
lookup_expr 的 icontains
表示 包含的意思。
查询URL:
http://localhost:8000/servers/?project=项目名
SearchFilter 搜索
搜索使用的是 Django rest framework 自己的 SearchFilter
同样在浏览器 API 中会展现出一个搜索控件,可以方便前端人员、测试人员的测试。
视图
from rest_framework import filters
class ServersSearchViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Server.objects.all()
serializer_class = ServerSerializer
filter_backends = [filters.SearchFilter]
search_fields = ['hostname', 'manufacturer']
URL 配置
router.register(r'servers-search', views.ServersSearchViewSet)
可以支持基于双下划线的夸表搜索
search_fields = ['hostname', 'manufacturer', ' 'disk__capacity']
这样会增加搜索 硬盘容量在指定范围的 服务器
支持组合搜索
搜索主机名还有 db 字符并且硬盘容量是 200 的服务器
搜索的逻辑关系
默认搜索的条件是模糊匹配的,就是包含的意思。
还支持以下逻辑关系
'^'
匹配开头。'='
完全匹配。'@'
全文搜索。(目前只支持Django的MySQL后端。)'$'
正则表达式搜索。
示例
search_fields = ['$hostname']
OrderingFilter 排序
class ServersSearchViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Server.objects.all()
serializer_class = ServerSerializer
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
search_fields = ['$hostname', 'manufacturer', 'disk__capacity']
ordering_fields = ["create_at", "disk__capacity"]
ordering = ["-create_at"] # 默认排序字段
少张图