DRF之过滤类源码分析
【一】过滤类介绍及BaseFilterBackend
- Django REST framework(DRF)中的过滤类允许你在API视图中对查询进行过滤,以根据特定条件筛选结果集。
- 过滤类是DRF的一部分,它允许你轻松地添加各种过滤选项,以满足不同用例的需求。
class BaseFilterBackend:
"""
A base class from which all filter backend classes should inherit.
"""
def filter_queryset(self, request, queryset, view):
"""
Return a filtered queryset.
"""
raise NotImplementedError(".filter_queryset() must be overridden.")
def get_schema_fields(self, view):
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`'
return []
def get_schema_operation_parameters(self, view):
return []
【二】内置过滤类SearchFilter
【1】使用
# 过滤:必须继承 GenericAPIView 及其子类,才能使用(如果继承APIView,则不能这么写)
# DRF 给我们提供了一个排序类
from rest_framework.filters import SearchFilter
class BookView(GenericViewSet, ListModelMixin):
queryset = models.Book.objects.all()
serializer_class = BookSerializer
filter_backends = [SearchFilter] # 排序类
# SearchFilter:必须配合一个类属性
# 按照那些字段进行筛选
# http://127.0.0.1:8000/app01/v1/books/?search=梦
# 只要name/price中带 梦 都能被搜出来
search_fields = ['name']
【2】源码分析
class SearchFilter(BaseFilterBackend):
# The URL query parameter used for the search.
# 搜索参数的名称,默认为 api_settings.SEARCH_PARAM,通常是 search。
# 拼在路由尾部的关键字 http://127.0.0.1:8000/app01/v1/books/?search=梦
# 你可以通过在API视图中自定义该参数来更改搜索参数的名称。
search_param = api_settings.SEARCH_PARAM
# 用于HTML渲染搜索部件的模板路径,默认为 'rest_framework/filters/search.html'。
template = 'rest_framework/filters/search.html'
# 定义了搜索条件的前缀和它们对应的查询操作。
# 例如,^ 表示 "istartswith"(不区分大小写的开始于)
# = 表示 "iexact"(不区分大小写的精确匹配) 使用居多
# @ 表示 "search"(全文搜索)
# $ 表示 "iregex"(不区分大小写的正则表达式匹配)。
lookup_prefixes = {
'^': 'istartswith',
'=': 'iexact',
'@': 'search',
'$': 'iregex',
}
#
search_title = _('Search')
#
search_description = _('A search term.')
# 从视图中获取搜索字段列表
# 可以覆盖此方法以动态更改搜索字段
def get_search_fields(self, view, request):
"""
Search fields are obtained from the view, but the request is always
passed to this method. Sub-classes can override this method to
dynamically change the search fields based on request content.
"""
# 从视图类中中的 search_fields 列表中映射出所有的 过滤参数字段
return getattr(view, 'search_fields', None)
# 解析查询参数中的搜索条件,将其分割成一个列表。
def get_search_terms(self, request):
"""
Search terms are set by a ?search=... query parameter,
and may be comma and/or whitespace delimited.
"""
# 获取到路由尾部携带的参数
params = request.query_params.get(self.search_param, '')
# 空格代替 空
params = params.replace('\x00', '') # strip null characters
# 多字段进行切分
params = params.replace(',', ' ')
# 返回切分后的参数
return params.split()
# 构建搜索条件,根据前缀选择合适的查询操作。
# field_name 参数是传递给过滤器的搜索字段名,可能包含前缀
def construct_search(self, field_name):
# 字典定义了搜索条件的前缀和它们对应的查询操作。
# 检查 field_name 的第一个字符(前缀),并使用 get 方法从 lookup_prefixes 字典中获取对应的查询操作。
lookup = self.lookup_prefixes.get(field_name[0])
# 如果前缀存在
if lookup:
# 则将前缀从 field_name 中去除,并将查询操作存储在 lookup 变量中。
field_name = field_name[1:]
else:
# 如果前缀不存在,则默认使用 'icontains' 查询操作,这表示执行不区分大小写的部分匹配。
lookup = 'icontains'
# 使用 LOOKUP_SEP 连接字段名和查询操作,返回最终的搜索条件字符串。
# LOOKUP_SEP 是Django中用于连接查询字段和操作的分隔符,通常是双下划线 __。
# 例如,如果传递的搜索字段名是 ^name,那么这个方法将返回 "name__istartswith",这表示要在 name 字段上执行不区分大小写的开始于匹配操作。
return LOOKUP_SEP.join([field_name, lookup])
# 返回一个布尔值,指示是否应该使用 distinct() 方法来查询结果集以避免重复项。
# queryset 参数是要进行过滤的查询集,通常是数据库查询的结果集。
# search_fields 参数是用于搜索的字段列表,这些字段可能包含前缀。
def must_call_distinct(self, queryset, search_fields):
"""
Return True if 'distinct()' should be used to query the given lookups.
"""
# 迭代 search_fields 中的每个搜索字段
for search_field in search_fields:
# 对于每个搜索字段,它获取与查询集相关联的模型的元数据(opts = queryset.model._meta)
opts = queryset.model._meta
# 检查搜索字段的第一个字符是否在 lookup_prefixes 中
if search_field[0] in self.lookup_prefixes:
# 如果前缀存在,则将前缀从搜索字段中去除(search_field = search_field[1:])。
search_field = search_field[1:]
# Annotated fields do not need to be distinct
# 检查搜索字段是否已经在查询集的注释中。
if isinstance(queryset, models.QuerySet) and search_field in queryset.query.annotations:
# 如果字段已经在注释中,说明该字段已经被标记为注释字段,通常不需要使用 distinct()。
continue
# 如果字段不在注释中,则进一步检查字段是否包含嵌套关系(例如,related_field__nested_field)。
parts = search_field.split(LOOKUP_SEP)
# 如果字段是嵌套关系,它会更新模型的元数据以跟踪关系路径,并检查是否存在多对多关系(m2m 关系)。
for part in parts:
field = opts.get_field(part)
if hasattr(field, 'get_path_info'):
# This field is a relation, update opts to follow the relation
path_info = field.get_path_info()
opts = path_info[-1].to_opts
# # 如果字段是多对多关系,就需要调用 distinct() 方法,因为多对多关系通常会导致结果集中的重复项。
if any(path.m2m for path in path_info):
# This field is a m2m relation so we know we need to call distinct
# 如果需要调用 distinct(),它将返回 True
return True
else:
# This field has a custom __ query transform but is not a relational field.
break
# 最后,方法返回一个布尔值,指示是否应该调用 distinct()。
# 如果需要调用 distinct(),它将返回 True,否则返回 False。
return False
# 实际的查询过滤操作,根据查询参数中的搜索条件修改查询集
def filter_queryset(self, request, queryset, view):
# 获取了视图中定义的搜索字段列表(search_fields)和查询参数中传递的搜索条件(search_terms)。
search_fields = self.get_search_fields(view, request)
search_terms = self.get_search_terms(request)
# 如果没有定义搜索字段或者没有传递搜索条件
if not search_fields or not search_terms:
# 就直接返回原始的查询集 queryset,不进行任何过滤。
return queryset
# 构建了一个包含了所有搜索字段的ORM查询操作列表 orm_lookups
# 通过调用 construct_search 方法将搜索字段名转换为相应的查询操作。
orm_lookups = [
self.construct_search(str(search_field))
for search_field in search_fields
]
# 始化了两个变量,base 和 conditions。
# base 是原始的查询集
# conditions 是用来存储过滤条件的列表。
base = queryset
conditions = []
# 迭代每个搜索条件(search_term)和每个查询操作(orm_lookup),并创建一个 Q 对象,将查询操作和搜索条件传递给它。
# 这个 Q 对象表示了一个或多个查询条件的逻辑或关系。
for search_term in search_terms:
# 创建的 Q 对象列表存储在 queries 中
queries = [
models.Q(**{orm_lookup: search_term})
for orm_lookup in orm_lookups
]
# 然后使用 reduce(operator.or_, queries) 将它们组合成一个包含所有查询条件的 Q 对象。
# 最终,conditions 列表包含了一个或多个 Q 对象,每个 Q 对象代表一个搜索条件的查询操作。
# 使用 reduce(operator.and_, conditions) 将所有的 Q 对象组合成一个包含所有搜索条件的查询操作,这个查询操作表示了所有搜索条件之间的逻辑与关系。
conditions.append(reduce(operator.or_, queries))
# 最后,使用 queryset.filter() 方法,将这个复合查询操作应用于原始查询集,以便执行过滤操作。
queryset = queryset.filter(reduce(operator.and_, conditions))
# 如果必须调用 distinct() 方法(通过调用 must_call_distinct 方法来确定)
if self.must_call_distinct(queryset, search_fields):
# Filtering against a many-to-many field requires us to
# call queryset.distinct() in order to avoid duplicate items
# in the resulting queryset.
# We try to avoid this if possible, for performance reasons.
# 在过滤后的查询集上调用 distinct() 以确保结果集不包含重复项
queryset = distinct(queryset, base)
# 返回过滤后的数据集
return queryset
# 用于生成搜索框的HTML表示形式,以便在API的浏览器浏览界面中显示
def to_html(self, request, queryset, view):
'''
目的是生成一个包含搜索框的HTML表示,以便在API的浏览器浏览界面中让用户输入搜索条件。
搜索框的样式和布局通常由指定的模板文件定义,这使得可以根据需要自定义搜索框的外观。
搜索参数的名称和搜索条件的值也被传递到模板中,以便在HTML中动态生成搜索框。
'''
# 检查视图是否定义了搜索字段列表(search_fields)。
if not getattr(view, 'search_fields', None):
# 如果没有定义搜索字段,就直接返回空字符串,表示不需要显示搜索框。
return ''
# 获取查询参数中传递的搜索条件(search_terms)。
term = self.get_search_terms(request)
# 如果搜索条件存在,就将第一个搜索条件(通常只支持一个搜索条件)存储在 term 变量中。
term = term[0] if term else ''
# 创建一个名为 context 的字典,其中包含两个键值对:
# 'param':用于指定搜索参数的名称,通常是 search。
# 'term':包含了搜索条件的值,即用户在搜索框中输入的文本。
context = {
'param': self.search_param,
'term': term
}
# 使用 Django 的 loader.get_template 函数获取指定模板文件(self.template)的模板对象。
# 这个模板通常包含了搜索框的HTML表示。
template = loader.get_template(self.template)
# 使用 template.render(context) 渲染模板,将 context 字典中的数据传递给模板,生成包含搜索框的HTML表示
return template.render(context)
def get_schema_fields(self, view):
'''
目的是生成一个包含搜索参数的字段定义,以便在API文档中显示搜索参数的相关信息。
这有助于API用户理解如何使用搜索功能,并构建正确的搜索请求。
'''
# 通过 assert 语句确保 coreapi 和 coreschema 这两个库已经安装。
# 这些库是用于构建API文档和描述API数据结构的工具。
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`'
# 创建一个列表,其中包含一个 CoreAPI 字段定义对象。
# 这个字段定义对象用于描述搜索参数。
# 返回一个包含了搜索参数字段定义的列表。这个列表通常会与其他字段定义一起用于生成API文档,并且可以让API客户端了解如何构建搜索请求
return [
coreapi.Field(
# name:搜索参数的名称,通常是 search。
name=self.search_param,
# required:指定搜索参数是否是必需的,这里设置为 False 表示不是必需的。
required=False,
# location:指定搜索参数在请求中的位置,这里设置为 query,表示在查询字符串中。
location='query',
# schema:定义搜索参数的数据模式,这里使用 coreschema.String 来定义搜索参数的数据类型为字符串,并提供了标题(title)和描述(description)。
schema=coreschema.String(
title=force_str(self.search_title),
description=force_str(self.search_description)
)
)
]
# 用于生成API操作的参数定义,这些参数用于API文档的生成
# 返回包含了参数定义的列表。这个列表通常会与API操作一起用于生成API文档,以帮助API用户了解如何构建请求和使用参数。
def get_schema_operation_parameters(self, view):
'''
API文档中显示操作的参数信息。这有助于API用户理解如何构建API请求,并知道哪些参数是可选的、哪些是必需的
'''
# 创建了一个包含参数定义的列表。每个参数定义都是一个字典,描述了一个API操作的参数。
return [
{
# 'name':指定参数的名称,通常是 search。
'name': self.search_param,
# 'required':指定参数是否是必需的,这里设置为 False 表示不是必需的。
'required': False,
# 'in':指定参数在请求中的位置,这里设置为 query,表示在查询字符串中。
'in': 'query',
# 'description':提供参数的描述,使用了 force_str 函数来确保描述是字符串类型。
'description': force_str(self.search_description),
# 'schema':定义参数的数据模式,这里指定参数的数据类型为字符串('type': 'string')。
'schema': {
'type': 'string',
},
},
]
【三】第三方过滤类DjangoFilterBackend
【1】使用
# (2) 过滤:必须继承 GenericAPIView 及其子类,才能使用(如果继承APIView,则不能这么写)
# 第三方过滤类: pip3.9 install django-filter
from django_filters.rest_framework import DjangoFilterBackend
class BookView(GenericViewSet, ListModelMixin):
queryset = models.Book.objects.all()
serializer_class = BookSerializer
filter_backends = [DjangoFilterBackend] # 排序类
# SearchFilter:必须配合一个类属性
# 按照那些字段进行筛选
# http://127.0.0.1:8000/app01/v1/books/?search=梦
# 根据指定字段进行筛选,按名字和价格精准匹配
filterset_fields = ['name', 'price']
【2】源码分析
class DjangoFilterBackend:
# 用于指定用于过滤的过滤器集的基类,默认为filterset.FilterSet
filterset_base = filterset.FilterSet
# 指示在过滤器验证失败时是否引发异常,默认为True,表示引发异常。
raise_exception = True
@property
# 用于确定渲染过滤器表单时要使用的模板
def template(self):
# 如果使用Crispy Forms
if compat.is_crispy():
# 则为"django_filters/rest_framework/crispy_form.html"
return "django_filters/rest_framework/crispy_form.html"
# 否则为"django_filters/rest_framework/form.html"。
return "django_filters/rest_framework/form.html"
# 返回过滤器集的实例
def get_filterset(self, request, queryset, view):
# 使用get_filterset_class方法来获取用于过滤查询集的FilterSet类。
# 这个类可以在视图中通过filterset_class属性指定,如果未指定,则会自动创建一个基于查询集的FilterSet类。
filterset_class = self.get_filterset_class(view, queryset)
# 检查过滤器集类:如果获取到了过滤器集类(filterset_class不为None),则继续执行下面的步骤。
# 否则,返回None,表示没有可用的过滤器集。
if filterset_class is None:
return None
# 获取过滤器集的关键字参数
# 调用get_filterset_kwargs方法,以获取传递给过滤器集构造函数的关键字参数
# 。这些参数通常包括请求数据、查询集和请求对象等。
kwargs = self.get_filterset_kwargs(request, queryset, view)
# 创建过滤器集对象
# 最后,它使用获取到的过滤器集类和关键字参数来创建过滤器集对象,并将其返回。
return filterset_class(**kwargs)
# 返回用于过滤查询集的FilterSet类
def get_filterset_class(self, view, queryset=None):
"""
# 这个方法会根据视图和查询集的情况来确定应该使用哪个 FilterSet 类
Return the `FilterSet` class used to filter the queryset.
"""
# 尝试从视图中获取 filterset_class 属性。这个属性是视图中定义的,可以指定要用于过滤的 FilterSet 类。
# 如果视图中有指定,则直接返回这个类。
filterset_class = getattr(view, "filterset_class", None)
# 如果视图中没有指定 filterset_class,它会检查是否有 filterset_fields 属性。
# filterset_fields 属性是一组用于过滤的字段名称,通常与查询集的模型相关。
filterset_fields = getattr(view, "filterset_fields", None)
if filterset_class:
filterset_model = filterset_class._meta.model
# FilterSets do not need to specify a Meta class
if filterset_model and queryset is not None:
assert issubclass(
queryset.model, filterset_model
), "FilterSet model %s does not match queryset model %s" % (
filterset_model,
queryset.model,
)
return filterset_class
# 如果视图中定义了这个属性
if filterset_fields and queryset is not None:
# 它会自动创建一个临时的 FilterSet 类,该类的 Meta 类中包含了模型和字段信息。
MetaBase = getattr(self.filterset_base, "Meta", object)
class AutoFilterSet(self.filterset_base):
class Meta(MetaBase):
model = queryset.model
fields = filterset_fields
return AutoFilterSet
# 如果既没有指定 filterset_class 也没有 filterset_fields,则返回 None,表示没有可用的 FilterSet 类。
return None
# 此方法返回传递给过滤器集构造函数的关键字参数,包括请求数据、查询集和请求对象。
def get_filterset_kwargs(self, request, queryset, view):
return {
"data": request.query_params,
"queryset": queryset,
"request": request,
}
# 此方法用于过滤查询集。,,。
def filter_queryset(self, request, queryset, view):
# 通过调用 get_filterset 方法,获取与视图关联的过滤器集实例 filterset。
filterset = self.get_filterset(request, queryset, view)
# 检查 filterset 是否为 None。
if filterset is None:
# 如果 filterset 为 None,说明没有可用的过滤器集与视图关联,此时直接返回原始的查询集 queryset,不进行任何过滤。
return queryset
# 如果 filterset 存在,方法会进一步验证过滤器集的有效性,即调用 is_valid 方法检查过滤器集是否通过了验证。
if not filterset.is_valid() and self.raise_exception:
# 如果过滤器集无效(is_valid 返回 False)并且 self.raise_exception 属性为 True,则会抛出验证异常,该异常包含了过滤器集的错误信息。
raise utils.translate_validation(filterset.errors)
# 然后将其应用于查询集
return filterset.qs
# 此方法返回过滤器表单的HTML表示。
def to_html(self, request, queryset, view):
# 调用 get_filterset 方法获取过滤器集实例 filterset。
filterset = self.get_filterset(request, queryset, view)
# 如果 filterset 为 None,则返回 None,表示没有可用的过滤器集,因此无法生成过滤器表单的 HTML 表示。
if filterset is None:
return None
# 如果 filterset 存在,方法会根据指定的模板(self.template)渲染过滤器表单。
# 通常,模板会包含 HTML 表单元素,以显示过滤器字段和相应的输入框、复选框等表单组件。
template = loader.get_template(self.template)
context = {"filter": filterset}
# 最终,方法返回渲染后的 HTML 表示,以便在前端页面中显示过滤器表单
return template.render(context, request)
# 此方法返回与过滤器字段相关的CoreAPI字段定义
def get_coreschema_field(self, field):
# 首先,它根据过滤器字段的类型判断,
if isinstance(field, filters.NumberFilter):
# 如果字段类型是 filters.NumberFilter,则生成一个 compat.coreschema.Number 字段
field_cls = compat.coreschema.Number
else:
# 否则生成一个 compat.coreschema.String 字段。
field_cls = compat.coreschema.String
# 字段的 description 属性通常设置为过滤器字段的帮助文本(help_text),以提供关于字段用途的描述信息。
# 最终,方法返回生成的 CoreAPI 字段定义,用于 API 文档的生成和展示
return field_cls(description=str(field.extra.get("help_text", "")))
# 此方法返回API操作的参数定义列表,用于生成API文档。它检查过滤器集的基本过滤器,并为每个字段创建一个参数定义。
def get_schema_fields(self, view):
# This is not compatible with widgets where the query param differs from the
# filter's attribute name. Notably, this includes `MultiWidget`, where query
# params will be of the format `<name>_0`, `<name>_1`, etc...
# 检查 Django 的 RemovedInDjangoFilter25Warning
from django_filters import RemovedInDjangoFilter25Warning
# 并发出警告,指出内置的模式生成已被弃用,建议使用 drf-spectacular。
warnings.warn(
"Built-in schema generation is deprecated. Use drf-spectacular.",
category=RemovedInDjangoFilter25Warning,
)
# 断言 coreapi 和 coreschema 库已安装,因为这些库用于生成 API 文档的参数字段。
assert (
compat.coreapi is not None
), "coreapi must be installed to use `get_schema_fields()`"
assert (
compat.coreschema is not None
), "coreschema must be installed to use `get_schema_fields()`"
# 方法尝试从视图中获取查询集(queryset)对象。
try:
# 这是通过调用视图的 get_queryset() 方法来完成的,以便获取与视图关联的查询集。
queryset = view.get_queryset()
except Exception:
# 如果无法获取查询集(通常是因为视图没有实现 get_queryset() 方法),则将 queryset 设置为 None
queryset = None
# 并发出警告,指出该视图不兼容模式生成。
warnings.warn(
"{} is not compatible with schema generation".format(view.__class__)
)
# 使用 get_filterset_class 方法来获取与视图关联的过滤器集类(filterset_class)
filterset_class = self.get_filterset_class(view, queryset)
return (
[]
# 如果过滤器集类已经在视图中定义(通过 filterset_class 属性),则使用该类
if not filterset_class
# 如果没有可用的过滤器集类,方法返回一个空列表 [],表示没有需要生成参数字段的过滤器。
else [
# 否则,如果视图定义了 filterset_fields 属性且查询集不为 None,则会动态创建一个自动生成的过滤器集类 AutoFilterSet,该类继承自 filterset_base,并使用查询集的模型和 filterset_fields 属性来定义过滤器集。
compat.coreapi.Field(
# name 属性设置为字段的名称。
name=field_name,
# required 属性根据字段的 required 属性设置。
required=field.extra["required"],
# location 属性设置为 "query",表示这些参数位于查询字符串中
location="query",
# schema 属性通过调用 get_coreschema_field 方法生成,该方法生成与字段相关的 CoreAPI 字段定义
schema=self.get_coreschema_field(field),
)
#
for field_name, field in filterset_class.base_filters.items()
]
)
# 此方法返回API操作的参数列表,用于生成API文档。
# 它与get_schema_fields类似,但提供了更详细的参数信息,包括类型、是否必需等。
def get_schema_operation_parameters(self, view):
# 检查 Django 的 RemovedInDjangoFilter25Warning
from django_filters import RemovedInDjangoFilter25Warning
# 并发出警告,指出内置的模式生成已被弃用,建议使用 drf-spectacular
warnings.warn(
"Built-in schema generation is deprecated. Use drf-spectacular.",
category=RemovedInDjangoFilter25Warning,
)
try:
# 尝试从视图中获取查询集(queryset)对象,以便确定与视图关联的模型。
queryset = view.get_queryset()
except Exception:
# 如果无法获取查询集(通常是因为视图没有实现 get_queryset() 方法),则将 queryset 设置为 None
queryset = None
# 并发出警告,指出该视图不兼容模式生成。
warnings.warn(
"{} is not compatible with schema generation".format(view.__class__)
)
# 使用 get_filterset_class 方法来获取与视图关联的过滤器集类(filterset_class)
# 如果过滤器集类已经在视图中定义(通过 filterset_class 属性),则使用该类
# 否则,如果视图定义了 filterset_fields 属性且查询集不为 None,则会动态创建一个自动生成的过滤器集类 AutoFilterSet,该类继承自 filterset_base,并使用查询集的模型和 filterset_fields 属性来定义过滤器集。
filterset_class = self.get_filterset_class(view, queryset)
# 如果没有可用的过滤器集类,方法返回一个空列表 [],表示没有需要生成参数字段的过滤器。
if not filterset_class:
return []
parameters = []
# # 遍历过滤器集类的基本过滤器字段(base_filters)并为每个字段创建一个参数定义
for field_name, field in filterset_class.base_filters.items():
#
parameter = {
# name 属性设置为字段的名称。
"name": field_name,
# required 属性根据字段的 required 属性设置。
"required": field.extra["required"],
# in 属性设置为 "query",表示这些参数位于查询字符串中。
"in": "query",
# description 属性设置为字段的标签(label),如果字段没有标签,则设置为字段的名称。
"description": field.label if field.label is not None else field_name,
# schema 属性是一个包含参数类型信息的字典。在这里,所有参数都被表示为字符串("type": "string")。
"schema": {
"type": "string",
},
}
# 如果字段的 extra 属性包含了 "choices" 键(即字段具有选项)
if field.extra and "choices" in field.extra:
# 将 schema 字典的 "enum" 键设置为字段选项的列表。
parameter["schema"]["enum"] = [c[0] for c in field.extra["choices"]]
parameters.append(parameter)
return parameters
【四】自定义过滤类
【1】使用
from rest_framework import filters
from django.db.models import Q
class BookFilter(filters.BaseFilterBackend):
'''
def filter_queryset(self, request, queryset, view):
"""
Return a filtered queryset.
"""
raise NotImplementedError(".filter_queryset() must be overridden.")
'''
def filter_queryset(self, request, queryset, view):
# 返回的数据,都是过滤后的数据
# http://127.0.0.1:8000/app01/v1/books/?price=99&name=追梦赤子心
# 需要将筛选条件变成price=99或name=追梦赤子心,而第三方写法中访问地址只能是上面的格式
price = request.query_params.get('price')
name = request.query_params.get('name')
queryset = queryset.filter(Q(name=name) | Q(price=price))
return queryset
【2】分析
- 根据地址栏中传入的数据进行过滤
- 将过滤完的 queryset 数据集返回
本文来自博客园,作者:Chimengmeng,转载请注明原文链接:https://www.cnblogs.com/dream-ze/p/17713243.html