一.mixins泛型视图类
一般自定义视图类在使用mixins泛型类时也就是继承该类,还需要再继承一个GenericAPIView类搭配使用
1.1mixins源码介绍:
1.1.1 CreateModelMixin源码
class CreateModelMixin:
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
#get_serializer方法得到属性序列化类serializer_class()视图函数中你需要指定该属性
#serializer_class=你自定义的序列化类,这里没有使用many参数,不用一般默认False,
#也就是适合单对象的序列化
serializer = self.get_serializer(data=request.data)
#raise_exception=True验证当有异常时抛出异常
serializer.is_valid(raise_exception=True)
#调用下方perform_create方法进行保存对象
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
serializer.save()
def get_success_headers(self, data):
try:
return {'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
return {}
1.1.2 ListModelMixin源码
class ListModelMixin:
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
#get_queryset在GenericAPIView类,返回的是GenericAPIView视图类中queryset属性
#filter_queryset()得到的是后端过滤后的查询集(搭配过滤器使用)后面会讲
queryset = self.filter_queryset(self.get_queryset())
#paginate_queryset返回结果的单个页面,如果分页被禁用,则返回“None”。
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
1.1.3 RetrieveModelMixin源码
class RetrieveModelMixin:
"""
Retrieve a model instance.
"""
#该方法使用场景为url位置参数:类似于url(r'^books/(?P<pk>\d+)/$'
def retrieve(self, request, *args, **kwargs):
#GenericAPIView类中的get_object通过url位置参数返回一个单对象
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
1.1.4 UpdateModelMixin源码
class UpdateModelMixin:
"""
单实例更新:
1. 获取 kwargs 字典中 partial 的值,如果没有,则返回False,有,则返回值
2. 获取单个对象
3. 通过序列化,获取单个对象的序列化对象
4. 判断序列化对象的有效性
5. 更新数据
"""
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
return Response(serializer.data)
def perform_update(self, serializer):
serializer.save()
def partial_update(self, request, *args, **kwargs):
#这里给了一个partial为Ture,然后调用update()
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
1.1.4 DestroyModelMixin源码
class DestroyModelMixin:
"""
删除单对象数据
"""
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
def perform_destroy(self, instance):
instance.delete()
1.2 视图集viewset的源码:
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
"""
默认情况下,GenericViewSet类不提供任何操作,
但包含通用视图行为的基本集,如
' get_object '和' get_queryset '方法。
"""
pass
class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
提供默认的' list() '和' retrieve() '动作的视图集.
"""
pass
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
一个视图集提供了默认的' create() ', ' retrieve() ', ' update() ',
' partial_update() ', ' destroy() '和' list() '操作。
"""
pass
1.3 generic相关视图的介绍:
1.3.1 视图架构图
1.3.1 视图源码
class CreateAPIView(mixins.CreateModelMixin,
GenericAPIView):
"""
Concrete view for creating a model instance.
"""
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
class RetrieveAPIView(mixins.RetrieveModelMixin,
GenericAPIView):
"""
Concrete view for retrieving a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
class DestroyAPIView(mixins.DestroyModelMixin,
GenericAPIView):
"""
Concrete view for deleting a model instance.
"""
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
class UpdateAPIView(mixins.UpdateModelMixin,
GenericAPIView):
"""
Concrete view for updating a model instance.
"""
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
class ListCreateAPIView(mixins.ListModelMixin,
mixins.CreateModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset or creating a model instance.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class RetrieveUpdateAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
GenericAPIView):
"""
Concrete view for retrieving, updating a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
class RetrieveDestroyAPIView(mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
GenericAPIView):
"""
Concrete view for retrieving or deleting a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericAPIView):
"""
Concrete view for retrieving, updating or deleting a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
二.视图中所涉及的重要的源码
2.1 GenericAPIView:
class GenericAPIView(views.APIView):
"""
Base class for all other generic views.
"""
#你需要设置这些属性,
#或重写' get_queryset() ' / ' get_serializer_class() '。
#如果你重写一个视图方法,它是重要的调用
# ' get_queryset() '而不是直接访问' queryset '属性,
# as ' queryset '只会被求值一次,并且这些结果会被缓存
#对于所有后续请求。
queryset = None
serializer_class = None
#如果你想使用对象查找而不是pk,设置'lookup_field'。
#对于更复杂的查找需求,需重写' get_object() '。
lookup_field = 'pk' #默认为pk也就是主键
lookup_url_kwarg = None #url中的位置参数
#用于查询集过滤的过滤后端类(往往这里常用的就是结合django的settings中配置REST_FRAMEWORK = {
#'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']},属于
#全局变量,你也可以不选择全局配置,在自己的视图集类下给该属性赋值:filter_backends = [DjangoFi
#lterBackend]
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
# 用于查询集分页的样式。
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
def get_queryset(self):
"""
获取此视图的项目列表。
这必须是一个可迭代对象,并且是一个查询集。
默认使用' self.queryset '。
应该一直使用这个方法而不是访问self.queryset
直接self.queryset '只求值一次,这些结果
为所有后续请求缓存。
如果需要提供不同的服务,您可能希望重写此选项
查询集,取决于传入的请求。
(如。返回特定于用户的项目列表)
"""
assert self.queryset is not None, (
"'%s' should either include a `queryset` attribute, "
"or override the `get_queryset()` method."
% self.__class__.__name__
)
queryset = self.queryset
if isinstance(queryset, QuerySet):
# 确保对每个请求重新评估queryset.
queryset = queryset.all()
return queryset
def get_object(self):
"""
返回视图显示的对象。
如果您需要提供非标准的,您可能需要重写它
queryset查找。例如,如果对象使用multiple引用
url conf中的关键字参数。
"""
#得到过滤后查询集赋值给queryset
queryset = self.filter_queryset(self.get_queryset())
# Perform the lookup filtering.
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
#断言位置参数有没有在kwargs
assert lookup_url_kwarg in self.kwargs, (
'Expected view %s to be called with a URL keyword argument '
'named "%s". Fix your URL conf, or set the `.lookup_field` '
'attribute on the view correctly.' %
(self.__class__.__name__, lookup_url_kwarg)
)
filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
obj = get_object_or_404(queryset, **filter_kwargs)
# 权限的验证函数
self.check_object_permissions(self.request, obj)
return obj
def get_serializer(self, *args, **kwargs):
"""
返回应该用于验证和的序列化器实例,反序列化输入和序列化输出。
例如如果想要序列化list多对象的创建入库,可以重写该方法,在其中进行request.data
为列表时,返回serializer_class(many=True, *args, **kwargs)加入many参数
"""
#从下方get_serializer_class看出最后得到了self.serializer_class
#相当于serializer_class=self.serializer_class,把类属性serializer_class赋给了serializer_class
serializer_class = self.get_serializer_class()
#kwargs设置默认值,也就是需要序列化的额外内容context
kwargs.setdefault('context', self.get_serializer_context())
#大家可以看到此时返回的序列化类的实例
return serializer_class(*args, **kwargs)
def get_serializer_class(self):
"""
断言不为空时返回序列化类名,反之就返回类型应该包含serializer_class属性
或者重写get_serializer_class方法的异常字样
"""
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)
return self.serializer_class
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
序列化类提供额外的序列参数context上下文
"""
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}
def filter_queryset(self, queryset):
'''
给定一个查询集,使用过滤后端类及其下方法过滤它。返回得到过滤后的查询集,如filter_backends
指定的是DjangoFilterBackend,其实它调用了原本属于源码DjangoFilterBackend类下的filter_qu
eryset,其中backend代表我自定义的filterset类,filter_backends属性代表我们自定义的filter
set类的列表,然后使用backend()也就是自定义的过滤集类的对象调用filter_queryset,返回过滤后
的查询集赋值给了queryset
'''
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
#此装饰器的作用就是将函数转变成属性
@property
def paginator(self):
"""
返回与视图关联的分页器实例,或者“None”。
"""
if not hasattr(self, '_paginator'):
if self.pagination_class is None:
self._paginator = None
else:
self._paginator = self.pagination_class()
return self._paginator
def paginate_queryset(self, queryset):
"""
返回结果的单个页面,如果分页被禁用,则返回“None”。
其中的self.paginator代表的是分页类的实例,然后调用分页类源码下paginate_queryset()
返回的是页面对象的列表
"""
if self.paginator is None:
return None
return self.paginator.paginate_queryset(queryset, self.request, view=self)
def get_paginated_response(self, data):
"""
返回给定输出数据的分页样式' Response '对象。
"""
assert self.paginator is not None
return self.paginator.get_paginated_response(data)
2.3 PageNumberPagination:
class PageNumberPagination(BasePagination):
"""
一个简单的基于页码的样式,它支持如下的页码
查询参数。例如:
http://api.example.org/accounts/?page=4
http://api.example.org/accounts/?page=4&page_size=100
"""
# The default page size.
# 默认为 `None`, 意思是分页被禁用.
page_size = api_settings.PAGE_SIZE
#django_paginator_class赋值了Django 分页器类(DjangoPaginator)
django_paginator_class = DjangoPaginator
# 客户端可以使用此查询参数控制页面.
page_query_param = 'page'
page_query_description = _('A page number within the paginated result set.')
#客户端可以使用这个查询参数来控制页面大小。
#默认为“None”。设置为eg 'page_size'以启用使用。
page_size_query_param = None
#每页返回的结果数
page_size_query_description = _('Number of results to return per page.')
#设置一个整数来限制客户端可能请求的最大页面大小。
#只有当设置了'page_size_query_param'时才相关.
max_page_size = None
#字符串值的列表或元组,指可与请求集合中最后一页一起使用的值。默认为page_query_param('last',)
last_page_strings = ('last',)
#在可浏览 API 中呈现分页控件时要使用的模板的名称。可以重写以修改呈现样式,
#或设置为完全禁用 HTML 分页控件。
template = 'rest_framework/pagination/numbers.html'
invalid_page_message = _('Invalid page.')
#该方法将传递初始查询集参数,并应返回一个可迭代对象。该对象仅包含所请求页面(本页面)中的数据
def paginate_queryset(self, queryset, request, view=None):
"""
如果需要,对查询集进行分页,或者返回一个页面对象
如果没有为此视图配置分页,则为' None '。
"""
#获取页面的大小
page_size = self.get_page_size(request)
if not page_size: #如果不存在返回为none
return None
paginator = self.django_paginator_class(queryset, page_size)
page_number = self.get_page_number(request, paginator)
try:
self.page = paginator.page(page_number)
except InvalidPage as exc:
msg = self.invalid_page_message.format(
page_number=page_number, message=str(exc)
)
raise NotFound(msg)
if paginator.num_pages > 1 and self.template is not None:
# The browsable API should display pagination controls.
self.display_page_controls = True
self.request = request
return list(self.page)
#返回分页的页数
def get_page_number(self, request, paginator):
page_number = request.query_params.get(self.page_query_param, 1)
if page_number in self.last_page_strings:
page_number = paginator.num_pages
return page_number
#该方法将传递一个序列化的页面数据,并应返回一个实例
def get_paginated_response(self, data):
return Response(OrderedDict([
('count', self.page.paginator.count),
('next', self.get_next_link()),
('previous', self.get_previous_link()),
('results', data)
]))
#返回一个页面显示的模式样板
def get_paginated_response_schema(self, schema):
return {
'type': 'object',
'properties': {
'count': {
'type': 'integer',
'example': 123,
},
'next': {
'type': 'string',
'nullable': True,
'format': 'uri',
'example': 'http://api.example.org/accounts/?{page_query_param}=4'.format(
page_query_param=self.page_query_param)
},
'previous': {
'type': 'string',
'nullable': True,
'format': 'uri',
'example': 'http://api.example.org/accounts/?{page_query_param}=2'.format(
page_query_param=self.page_query_param)
},
'results': schema,
},
}
#获取页面的容量大小
def get_page_size(self, request):
if self.page_size_query_param:
try:
return _positive_int(
request.query_params[self.page_size_query_param],
strict=True,
cutoff=self.max_page_size
)
except (KeyError, ValueError):
pass
return self.page_size
#在结果中返回下一个页面链接地址
def get_next_link(self):
if not self.page.has_next():
return None
url = self.request.build_absolute_uri()
page_number = self.page.next_page_number()
return replace_query_param(url, self.page_query_param, page_number)
#在结果中返回上一个页面链接地址
def get_previous_link(self):
if not self.page.has_previous():
return None
url = self.request.build_absolute_uri()
page_number = self.page.previous_page_number()
if page_number == 1:
return remove_query_param(url, self.page_query_param)
return replace_query_param(url, self.page_query_param, page_number)
2.3 Paginator:
提供参考的第三方举例详情地址https://www.jianshu.com/p/e9686192e7ee
class Paginator:
# Translators: 字符串,用于替换省略页中的省略页码
# 分页器生成的范围, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10].
ELLIPSIS = _('…')
def __init__(self, object_list, per_page, orphans=0,
allow_empty_first_page=True):
self.object_list = object_list
self._check_object_list_is_ordered()
self.per_page = int(per_page) #每页中允许的最大对象数。必选参数
self.orphans = int(orphans) #这是一个缺省参数,如果最后一页的数据小于这个值,会合并到上一页
self.allow_empty_first_page = allow_empty_first_page #允许首页为空 ,默认为True
def __iter__(self):
for page_number in self.page_range:
yield self.page(page_number)
def validate_number(self, number):
"""校验给的基于1的页数"""
try:
#先判断页面数是不是float并且不是整形抛出异常
if isinstance(number, float) and not number.is_integer():
raise ValueError
number = int(number)
except (TypeError, ValueError):
raise PageNotAnInteger(_('That page number is not an integer'))
#如果页数小于1,抛出EmptyPage
if number < 1:
raise EmptyPage(_('That page number is less than 1')
#如果页数大于总页数,抛出EmptyPage
if number > self.num_pages:
#这个就是属于结果为0的情况没有一个页,并且允许首页为空就pass,否则抛出空页面异常
if number == 1 and self.allow_empty_first_page:
pass
else:
raise EmptyPage(_('That page contains no results'))
return number
def get_page(self, number):
"""
返回指定页的page对象,页码从1开始。该方法可处理超出范围或无效的页码
如果给定页码不是数字则返回第一页,如果给定页码小于1或大于总页数,则返回最后一页
"""
try:
#先得到一个通过验证的页数值
number = self.validate_number(number)
#给出不是整形的异常,则给number赋值1
except PageNotAnInteger:
number = 1
#给出空页异常处理,则给number赋值为总页数也就是最后一页
except EmptyPage:
number = self.num_pages
return self.page(number)
def page(self, number):
"""返回指定页的page对象,它不能处理超出范围或无效页码,指定的页码无效时会触发validate_number异常."""
number = self.validate_number(number)
#bottom值代表的是指定页中的第一个对象在self.object_list列表中的下标
#不代表该页面第一个对象所排序的位置,例如一共5个对象,一个页面容量2个对象,总共就是3个页面
#第二个页面的第一个也就是起始对象的序号是3
bottom = (number - 1) * self.per_page
#top值代表的self.object_list列表中的区间的右边界值(但不包含)其实取的也就是该页的最后一个对象
top = bottom + self.per_page
#top + self.orphans(代表列表右边界值)如果大于等于所有页的总对象数,两边同时家去top值,相当于
#最后一页的对象数量小于等于self.orphans值,则将最后一页的对象数合并到前一页中
if top + self.orphans >= self.count:
top = self.count
#object_list[bottom:top]代表该页的所有对象
return self._get_page(self.object_list[bottom:top], number, self)
def _get_page(self, *args, **kwargs):
"""
返回单个页面的实例。
其中的Page是另一个源码类
"""
return Page(*args, **kwargs)
@cached_property #缓存属性的意思,类似于@property的效果
def count(self):
"""返回所有页面中的对象总数"""
c = getattr(self.object_list, 'count', None)
if callable(c) and not inspect.isbuiltin(c) and method_has_no_args(c):
return c()
return len(self.object_list)
@cached_property
def num_pages(self):
"""返回总页数."""
#如果总对象数=0,并且self.allow_empty_first_page不为True,返回0
if self.count == 0 and not self.allow_empty_first_page:
return 0
#max取最大值,其中self.count - self.orphans可能会看不懂为什么这么减,但是你只要验证符合页面数
#计算结果,例如count为5,orphans=2,差值hits就是3,那hits/self.per_page=1.5,你会发现页面数为2
#是符合原本应该为3页其最后一页为1个对象满足小于等于orphans值2,也就符合合并到前一页,成为2页。
hits = max(1, self.count - self.orphans)
#以整数形式返回该变量的上限值,如ceil(1.5)=2。
return ceil(hits / self.per_page)
@property
def page_range(self):
"""
返回页面迭代器,页码从1开始
"""
#self.num_pages+1为右边界值(开区间),为的是能取到最后一页的值
return range(1, self.num_pages + 1)
def _check_object_list_is_ordered(self):
"""
警告实例。object_list是无序的(通常是QuerySet)。
"""
ordered = getattr(self.object_list, 'ordered', None)
if ordered is not None and not ordered:
obj_list_repr = (
'{} {}'.format(self.object_list.model, self.object_list.__class__.__name__)
if hasattr(self.object_list, 'model')
else '{!r}'.format(self.object_list)
)
warnings.warn(
'Pagination may yield inconsistent results with an unordered '
'object_list: {}.'.format(obj_list_repr),
UnorderedObjectListWarning,
stacklevel=3
)
def get_elided_page_range(self, number=1, *, on_each_side=3, on_ends=2):
"""
返回一个基于1的页面范围,其中某些值被省略。
第一个参数number为当前页码数, on_each_side为当前页码左右两边的页数,on_ends为首尾页码范围。
如果当前页码为第10页,使用该方法将输出如下页码范围(page_range)。10左右两边各有3页,首尾各
有2页,其余页码号码用...代替。将page_range这个变量传递到前端模板进行遍历即可实现智能分页。
例如:[1, 2, '…', 7, 8, 9, 10, 11, 12, 13, '…', 49, 50]
"""
number = self.validate_number(number)
if self.num_pages <= (on_each_side + on_ends) * 2:
yield from self.page_range
return
if number > (1 + on_each_side + on_ends) + 1:
yield from range(1, on_ends + 1)
yield self.ELLIPSIS
yield from range(number - on_each_side, number + 1)
else:
yield from range(1, number + 1)
if number < (self.num_pages - on_each_side - on_ends) - 1:
yield from range(number + 1, number + on_each_side + 1)
yield self.ELLIPSIS
yield from range(self.num_pages - on_ends + 1, self.num_pages + 1)
else:
yield from range(number + 1, self.num_pages + 1)
2.4 DjangoFilterBackend(后端过滤类):
class DjangoFilterBackend(metaclass=RenameAttributes):
filterset_base = filterset.FilterSet
raise_exception = True
@property
def template(self):
if compat.is_crispy():
return 'django_filters/rest_framework/crispy_form.html'
return 'django_filters/rest_framework/form.html'
def get_filterset(self, request, queryset, view):
'''
先调用self.get_filterset_class获取视图中的过滤集类filterset_class
如果filterset_class为none,则返回none,除此之外就返回filterset_class(**kwargs)
'''
filterset_class = self.get_filterset_class(view, queryset)
if filterset_class is None:
return None
#调用self.get_filterset_kwargs返回一个字典有keys:queryset,request,data
#其中的data就是url后的过滤参数request.query_params
kwargs = self.get_filterset_kwargs(request, queryset, view)
return filterset_class(**kwargs)
def get_filterset_class(self, view, queryset=None):
"""
获取过滤集类
"""
#从我们定义的视图中获取filterset_class属性
filterset_class = getattr(view, 'filterset_class', None)
#从我们定义的视图中获取filterset_fields属性
filterset_fields = getattr(view, 'filterset_fields', None)
#例如我们自定义的视图类提供了filter_classs属性并赋值了自定义的过滤类
#如果filterset_class为none,并且定义的视图类中有filter_class属性
if filterset_class is None and hasattr(view, 'filter_class'):
utils.deprecate(
"`%s.filter_class` attribute should be renamed `filterset_class`."
% view.__class__.__name__)
#从试图类中获取filter_classs属性并赋值给filterset_class
filterset_class = getattr(view, 'filter_class', None)
#如果filterset_fields为none,并且定义的视图类中有filter_fields属性
if filterset_fields is None and hasattr(view, 'filter_fields'):
utils.deprecate(
"`%s.filter_fields` attribute should be renamed `filterset_fields`."
% view.__class__.__name__)
#从试图类中获取filter_fields属性并赋值给filterset_fields
filterset_fields = getattr(view, 'filter_fields', None)
#如果视图中的filterset_class属性不为空
if filterset_class:
filterset_model = filterset_class._meta.model
# FilterSets不需要指定一个元类
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
#如果视图中的filterset_fields属性和queryset不为none
if filterset_fields and queryset is not None:
MetaBase = getattr(self.filterset_base, 'Meta', object)
class AutoFilterSet(self.filterset_base):
class Meta(MetaBase):
model = queryset.model
fields = filterset_fields
return AutoFilterSet
return None
def get_filterset_kwargs(self, request, queryset, view):
'''
返回一个字典有data,queryset,request
request.query_params就是get请求url后拼接的过滤参数
'''
return {
'data': request.query_params,
'queryset': queryset,
'request': request,
}
def filter_queryset(self, request, queryset, view):
filterset = self.get_filterset(request, queryset, view)
if filterset is None:
return queryset
if not filterset.is_valid() and self.raise_exception:
raise utils.translate_validation(filterset.errors)
return filterset.qs
def to_html(self, request, queryset, view):
filterset = self.get_filterset(request, queryset, view)
if filterset is None:
return None
template = loader.get_template(self.template)
context = {'filter': filterset}
return template.render(context, request)
def get_coreschema_field(self, field):
if isinstance(field, filters.NumberFilter):
field_cls = compat.coreschema.Number
else:
field_cls = compat.coreschema.String
return field_cls(
description=str(field.extra.get('help_text', ''))
)
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...
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()`'
try:
queryset = view.get_queryset()
except Exception:
queryset = None
warnings.warn(
"{} is not compatible with schema generation".format(view.__class__)
)
filterset_class = self.get_filterset_class(view, queryset)
return [] if not filterset_class else [
compat.coreapi.Field(
name=field_name,
required=field.extra['required'],
location='query',
schema=self.get_coreschema_field(field)
) for field_name, field in filterset_class.base_filters.items()
]
def get_schema_operation_parameters(self, view):
try:
queryset = view.get_queryset()
except Exception:
queryset = None
warnings.warn(
"{} is not compatible with schema generation".format(view.__class__)
)
filterset_class = self.get_filterset_class(view, queryset)
if not filterset_class:
return []
parameters = []
for field_name, field in filterset_class.base_filters.items():
parameter = {
'name': field_name,
'required': field.extra['required'],
'in': 'query',
'description': field.label if field.label is not None else field_name,
'schema': {
'type': 'string',
},
}
if field.extra and 'choices' in field.extra:
parameter['schema']['enum'] = [c[0] for c in field.extra['choices']]
parameters.append(parameter)
return parameters