View类
代码
以下代码:参考笔记【1】request获取请求参数 :https://blog.csdn.net/Ataoker/article/details/130739635
from django.views import View
import json
from rest_framework.response import Response # 这个用不了,APIView才能用
from django.http import HttpResponse,JsonResponse
class IndexView(View):
def get(self,request, *args, **kwargs):
return JsonResponse({"this":"get()",
"msg":f"你的请求方法是{request.method}",
"【GET请求__常规的路径参数】常规获取参数方式":request.GET['name'],
"【GET请求__常规的路径参数】常规获取参数方式2":request.GET.get('name',None), # 推荐这种
"【GET请求__常规的路径参数】常规获取参数方式3":request.GET.getlist('name')
})
def post(self,request, *args, **kwargs):
return JsonResponse({"this":"post()",
"msg": f"你的请求方法是{request.method}",
"msg2": f"请求头Content_type是 {request.headers['Content-Type']}",
"【GET请求__常规的路径参数】常规获取参数方式2": request.GET.get('name', None),
"【POST请求__body中json字符串】常规获取参数方式___json字符串": f"内容是:{request.body}",
"【POST请求__body中json字符串】常规获取参数方式___字典": json.loads(request.body)
})
# def post(self, request, *args, **kwargs):
# return JsonResponse({"this": "psot()",
# "msg": f"你的请求方法是{request.method}",
# "msg2":f"请求头Content_type是 {request.headers['Content-Type']}",
# "【GET请求__常规的路径参数】常规获取参数方式2": request.GET.get('name', None),
# "【POST请求__x-www-form-urlencoded表单数据】常规获取参数方式1": request.POST.get('username', None),
# "【POST请求__x-www-form-urlencoded表单数据】常规获取参数方式2": request.POST["password"],
# })
def put(self, request, *args, **kwargs):
return JsonResponse({"this": "psot()","msg": f"你的请求方法是{request.method}"})
def delete(self, request,*args, **kwargs):
return JsonResponse({"this":"delete()","msg": f"你的请求方法是{request.method}"})
"""
url.py中,配置 path('test/',views.IndexView.as_view())
"""
验证代码作用
传递不同的请求方法,来验证View的功能
GET : http://127.0.0.1:8000/test/?name=xiaoli
POST(Content-Type:application/json ): http://127.0.0.1:8000/test/?name=xiaoli
- body中: {“age”:18}
POST(application/x-www-form-urlencoded): http://127.0.0.1:8000/test/?name=xiaoli
- 表单中 :username :taoke password : 123456
View类的效果展示:
get
post的json
post的表单
drf中最基础的视图类----APIView
它是继承了 View。所以可以定义get,post,put方法等
特点1:统一获取请求参数的方式
- get的 ?neme=xxx 参数,除了用 request.GET.get(“xx”) , 或者用 request.query_params.get(“xx”)
- post请求的 表单格式,或者是json格式数据,都可以用request.data (得到的是字典的形式) 来获取 数据。
- 好处:使用这个代码后,一种代码就能拿到不管是表单,不管是json格式的数据(表单组成字典, json字符串直接转成字典)
get
post的表单,这样请求,content-type会默认为表单
post的json,这样请求,content-type会默认为json
补充:如果是函数视图,可以加装饰器@api_view([‘POST’,‘GET’]), ---------->功能有点像APIview
- 函数中使用request.data 也能获取到body中的数据,
- 可以使用Resonse 进行返回(会渲染成网页)
特点2:可配置解析器
- 可以统一使用request.data 来获取 请求参数(有了这个解析器,才有特点1)
- 可以配置 默认解析器(DEFAULT_PARSER_CLASSES),来控制允许解析哪种请求参数。
rest_framework中的默认解析器,配置有三种(下图是源码restframework中的setting):
1. 全局配置
django提供了一个方式,就是可以不修改源码,而是在项目setting中进行修改, 写在如下 字典中、
REST_FRAMEWORK = {}
把以下放进去就可以
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser', # 假如不允许传这种就注释它, 想要传其他的,如xml,就要想办法加上
'rest_framework.parsers.MultiPartParser'
],
如下:进行了全局配置后的效果
2.局部配置
实际是修改源码,看APIView的源码就知道,默认是parser_classes 为 api_settings.DEFAULT_RENDERER_CLASSES,如图
而设置parser_classes ,就能让它不读 setting中的配置。
(需要引入from rest_framework.parsers import JSONParser,FormParser 优先级高于全局配置)
- 情况1:在类中,写parser_classes = [JSONParser,FormParser]
- 情况2:如果是函数, 就写装饰器 @parser_classes([JSONParser])
特点3:使用Response返回(配置渲染器)
使用from rest_framework.response import Response ,代替JsonResponse,它支持返回渲染多种格式的数据。
需要配置:DEFAULT_RENDERER_CLASSES
默认有两种:
- 一种是返回json格式
- 一种是页面的形式
可视化页面的配置登录按钮
1.登录
在外层url中,添加
from rest_framework.documentation import include_docs_urls
path('api/',include('rest_framework.urls')),
添加侯,这里就有个登录入口
2.增加格式渲染选择项(配不配置都可以?)
# 在app应用中的url中添加如下代码
from rest_framework.urlpatterns import format_suffix_patterns
urlpatterns= format_suffix_patterns(urlpatterns)
url中的format参数
定制渲染器_全局配置
可以增加其他的渲染方式,如:yml格式
'DEFAULT_RENDERER_CLASSES': [
'rest_framework_yaml.renderers.YAMLRenderer',
# 'rest_framework.renderers.JSONRenderer'
'rest_framework.renderers.BrowsableAPIRenderer',
]
yml格式,的解析器,和渲染器,需要安装如下:
定制渲染骑_局部配置
APIView的继承类----GenericAPIView
简单使用
自己先查看源码,找到GenericAPIView类,可以发现,它提供了一系列的的方法。
主要:
首先要定义两个类属性 queryset = None
和 serializer_class = None
get_queryset()
方法 是用来获取 querySet查询集。(支持重写,若不重写,它返回的值就是 类属性 queryset )get_serializer_class()
是用来获取序列化器类的。 (支持重写,若不重写,它返回的值就是 类属性 serializer_class )可以get_serializer_class()(instance=queryset ,data=xx)
,但一般可以直接get_serializer(instance=queryset ,data=xx)
继承GenericAPIView, 创建类时:
指定类属性 queryset 和 serializer_class。
使用时queryset,和serializer_class 时,用get_queryset()
和 get_serializer_class()
方法
定义的路由:默认是需要名为pk的, 也可以修改,如下(后期使用视图集,一般就不用改它了)
from rest_framework.generics import GenericAPIView
然后查看源码
如果没有lookup_url_kwarg, 就取 lookup_field 的 ‘pk’,图上的说明有点点问题
自定义
搜索排序
【b 和 c】 搜索排序_配置:
b 是关键字搜索过滤(默认为模糊)
c 是用某字段排序
d 是用某个字段来搜索 (默认为精确匹配)
1.setting中全局配置
'DEFAULT_FILTER_BACKENDS': [
"rest_framework.filters.SearchFilter", # 搜索过滤
# "rest_framework.filters.OrderingFilter" # 排序
],
'SEARCH_PARAM':'search', # 配置搜索的关键字参数名,默认是search(在rest_framework 中的setting能查看到)
'ORDERING_PARAM': 'ordering', # 配置排序的参数名,默认是ordering
---------------------------------------------------------------
2.类中局部配置(优先级高于全局):
filter_backends = [
filters.SearchFilter,
filters.OrderingFilter ]
search_fields = ["name", 'leader'] 允许做关键字查询的字段
ordering_fields = ['id','name','leader'] 允许进行排序的字段
b.搜索过滤的使用(类似关键字搜索):
源码
它提供了一个方法filter_queryset()
- 参数为:搜索过滤前的查询集
- 中间的处理:实际上用到了filter.py中SearchFilter() 的filter_queryset()
- 返回为:搜索过滤后的查询集
简单使用的效果
要使用filter_queryset()
,方法,就可以进行搜索,
用search 参数接收, 就去去筛选 search_fields 中的字段, 如下图是改了 setting中的配置的名称的
search_fields设置模糊查询
从源码中可以看出,可以设置 =, 还有 ^ 等
- 支持 以什么开头的查询, 如:search_fields =[“^name”] 这种写法就是name以某个字符串开头的查询 , =name 是精确匹配(源码在rest_framework的filter)
如:search?=停 就会过滤name为“停”开头的
- 也能通过关联表的字段来进行查询 search_fields = [“interfaces__name”, ‘leader’] 外键名__关联表的字段
如下:代码可用于搜索排序的练习(主要为下图)
class ProjectsView(GenericAPIView):
queryset = Projects.objects.all()
serializer_class = ProjectsModelSerializer
filter_backends = [
filters.SearchFilter,
filters.OrderingFilter
]
search_fields = ["name", 'leader']
ordering_fields = ['id','name','leader']
def get(self,request):
queryset = self.get_queryset()
# 使用过滤方法
filtered_queryset =self.filter_queryset(queryset)
s = self.get_serializer(instance=filtered_queryset, many=True) # 方式2
return Response(s.data )
c. 排序的使用
- 第一:类中添加 ordering_fields = [‘id’,‘name’,‘leader’] 表示允许这些字段排序
- 前端传参数 ordering=name (会以name进行排序),
- 前端传参数 ordering=-name 反着
- ordering=name,leader(会多重排序)
d 第三方的多字段筛选(默认为精准匹配)
使用
使用户能够根据一系列条件来检索数据
(这个可以和默认的过滤一起用的)
安装(官网中的)
pip install django-filter
, 然后注册 和全局配置
使用:
局部配置:
# 搜索、排序功能
filter_backends = [
filters.SearchFilter,
filters.OrderingFilter,
DjangoFilterBackend # 这个是原基础上增加的
]
可以改成模糊多字段搜索
略 官网:https://django-filter.readthedocs.io/en/stable/guide/usage.html
自定义【模型搜索类】
如下:可以让某个字段是模糊搜索、 效果是啥?重写的那部分?
补充:
使用时,局部还要指定filterset_class 指定自定义过滤骑
分页功能
使用源码中的分页类
- 第一:配置分页器( 默认为None,如果想分页就这样配置)
全局配置setting.py 中的REST_FRAMEWORK中
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 1 # 表示每页默认显示1个数据条数 (源碼分页器 、这个才生效)
局部配置类中进行设置(优先级高于全局)
from rest_framework.pagination import PageNumberPagination
pagination_class = PageNumberPagination
- 第二:使用
sefl.paginate_queryset()
来接受 查询集 , 返回的也是查询集- 这个方法 会用到 第一步, setting 中的配置, 每页几个
- 第三: 使用
self.get_paginated_response()
来代替Response()- 这样的话,返回的结构不一样
练习的代码在这里
from rest_framework.pagination import PageNumberPagination
class ProjectsView(GenericAPIView):
queryset = Projects.objects.all()
serializer_class = ProjectsModelSerializer
# 配置【搜索排序】--- rf_django中 默认为空,需要配置才生效
filter_backends = [filters.SearchFilter,filters.OrderingFilter]
# 搜索排序的代码
search_fields = ["name", 'leader']
ordering_fields = ['id','name','leader']
# 配置【分页器】-----默认为空需要配置
pagination_class = PageNumberPagination
def get(self,request):
# 支持搜索过滤
filtered_queryset=self.filter_queryset(self.get_queryset())
# 表示有分页的东西
paginated_querySet = self.paginate_queryset(filtered_queryset)
if paginated_querySet: # 如果有setting中的配置,才有,如果没有就为None了
s = self.get_serializer(instance=paginated_querySet,many=True)
return self.get_paginated_response(s.data)
s = self.get_serializer(instance=filtered_queryset,many=True)
return Response(s.data,status=status.HTTP_200_OK)
没有走分页的代码
走了分页的地方
自定义分页器:
一、自己写一个py文件
a.定义分页类:指定 page_size参数,和每页最大数,
实际上:就是重写 rest_framework 中 的pagination.py 中的PageNumberPagination 类
建议存的位置:
- 重写时,把这块代码复制出来,可以当道utils包中新建一个 pagination.py, 然后把代码放进来
- 做修改,自定类继承PageNumberPagination类:
from rest_framework.pagination import PageNumberPagination as _PageNumberPagination (名字换一下,自自定义类就还可以用原来的名字)
from rest_framework.pagination import PageNumberPagination as _PageNumberPagination
class PageNumberPagination(_PageNumberPagination):
page_size = 10
page_query_param = 'page'
page_query_description = '获取的页码'
page_size_query_param = "page_size"
page_size_query_description = '每一页数据条数'
max_page_size = 100
invalid_page_message = '无效页码'
b.修改 get_paginated_response()
它就在PageNumberPagination里面,所以也是可以修改的
def get_paginated_response(self, data):
response = super().get_paginated_response(data)
# 添加返回的字段
response.data['current_page_num'] = self.page.number # 当前页
response.data['total_pages'] = self.page.paginator.num_pages # 总页面数
# 删除、修改原有字段
response.data.pop('count')
# response.data.pop('next')
# response.data.pop('previous')
response.data['new_count']=self.page.paginator.count # 换名字
# 或者直接使用data参数,来像 父亲方法一样,自己来重写定义
# response = Response(data) # data是 列表套字典
# 再次返回
return response
或者这样写(这里可以发现,源码实际上用的就是Response 响应 )
# 重写,只返回count,和results, 增加当前页,和最大页
def get_paginated_response(self, data):
response = Response({
'count':self.page.paginator.count,
# 'next': self.get_next_link(),
# 'previous': self.get_previous_link(),
'results':data
})
# 修改
response.data['current_num'] =self.page.number # 添加一个current_num 参数
response.data['max_page']=self.page.paginator.num_pages
# 再次返回
return response
二、使用自定义分页类:
同样,可以全局配置,或者局部配置
全局配置:
'DEFAULT_PAGINATION_CLASS': 'utils.pagination.PageNumberPagination',
局部配置:
from utils.pagination import PageNumberPagination
pagination_class = PageNumberPagination
GenericAPIView扩展的两种方向
使用GenericAPIView就已经可以完成增删改查的 的接口封装了,如下
- 查询列表
- 新增
- 查询一个
- 更新一个
- 删除一个
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from .models import Projects,Interfaces
from .serializers import ProjectsModelSerializer
from rest_framework import status,filters
from rest_framework import mixins
# mixins.ListModelMixin,
class ProjectsView(GenericAPIView):
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
queryset = Projects.objects.all()
serializer_class = ProjectsModelSerializer
search_fields = ["name", 'leader']
ordering_fields = ['id','name','leader']
def get(self,request):
# 支持搜索过滤
filtered_queryset = self.filter_queryset(self.get_queryset())
# 表示有分页的东西
paginated_querySet = self.paginate_queryset(filtered_queryset)
if paginated_querySet: # 如果有setting中的配置,才有,如果没有就为None了
s = self.get_serializer(instance=paginated_querySet, many=True)
return self.get_paginated_response(s.data)
s = self.get_serializer(instance=filtered_queryset, many=True)
return Response(s.data, status=status.HTTP_200_OK)
#
# def get(self, request, *args, **kwargs):
# return self.list(request, *args, **kwargs)
def post(self,request):
s = self.get_serializer(data=request.data)
s.is_valid(raise_exception=True)
s.save()
return Response(s.data,status=status.HTTP_201_CREATED)
class ProjectsDetailView(GenericAPIView):
# def get_object(self,pk):
# try:
# project_data = Projects.objects.get(id=pk)
# return project_data
# except Exception as e:
# return Response({"msg":'参数有误'},status=400)
queryset = Projects.objects.all()
serializer_class = ProjectsModelSerializer
def get(self,request,pk):
try:
project_data = self.get_queryset().get(id=pk)
except Exception as e:
return Response({"msg": '参数有误'}, status=400)
s = self.get_serializer(instance=project_data)
return Response(s.data)
def put(self,request,pk):
try:
project_data = self.queryset.get(id=pk)
except Exception as e:
return Response({"msg": '参数有误'}, status=400)
s = self.serializer_class(instance=project_data,data=request.data)
s.is_valid(raise_exception=True)
s.save()
return Response(s.data,status=status.HTTP_201_CREATED)
def delete(self,reuest,pk):
try:
project_data = self.queryset.get(id=pk)
except Exception as e:
return Response({"msg": '参数有误'}, status=400)
project_data.delete()
return Response( {"msg": '删除成功'},status=status.HTTP_201_CREATED)
首先要知道,mixins.py(它抽离了增删改查的方法),什么意思看下面:
上面的代码中,可以发现:get, post ,get ,put delete 方法 和模型、无关,可以把它们抽离处理,restframework已经把这个抽离工作做好了
from rest_framework.generics import GenericAPIView ,点generics 可以进入到源码的地方
mixins.py
, 代码中:
- CreateModelMixin 中有create方法
- ListModelMixin 中有list方法
- RetrieveModelMixin 中有retrieve 方法
- UpdateModelMixin 中有update方法和 partial_update方法
- DestroyModelMixin 中有destroy方法
继续改造代码
见下图。继承ListModelMixin,+ GenericAPIView
这样后代码中就不用 再定义 def list()方法了,因为父类ListModelMixin 已经有了
可以发现,视图类中的代码更加简洁 :
A:一种方向的升级:各种通用视图View
就是把 Mixin 和 genericAPIView 一起结合来继承
在rest_framework 的 generics.py 再次优化了代码, 定义了一些类,继承了 genericAPIView , 和 mixins的 增删改查
rest_framework中的 generics.py文件中
from rest_framework.generics import GenericAPIView, ListCreateAPIView
代码又可以简化成如下这种,能实现增删改查
特点:
- url中定义要加 as_view(), 它作为的是类视图 (以下第二种,as_view()中是要加字典{})
B:另一种方向的升级:各种视图集 Set
因为:列表和新增不需要id, 查询和更新需要id
ModelViewSet(6个接口)、ReadOnlyModelViewSet(2个接口)
(后面只是说明是B方向怎么来的)
(由来) B 1
from rest_framework.viewsets import ViewSet 点击查看源码 ,然后点找到ViewSetMixin 类
他提供了一种配置url的方式 as_view(),括号里写字典
as_view({‘get’: ‘list’, ‘post’: ‘create’}) 一项为:请求方法: 具体的调用方法
当使用get, 就执行list方法
如下图,配置url 时, 如果是 ViewSet 的子类 ,as_view(), 里面就是加字典
,实际上是ViewSetMixin 提供的功能
以下代码可以验证此功能:,”根据请求路径 和 请求方法,来指定运行
(一个是视图集类
中的)某个方法
“
url中:
path('projects/', views.ProjectViewSet.as_view({
"get":"list_11",
"post":'create'
})),
path('projects/<int:pk>/', views.ProjectViewSet.as_view({
'get':"retrieve",
"put":"update",
"patch":"partial_update",
"delete":"destroy"
})),
view中
from rest_framework import viewsets
class ProjectViewSet(viewsets.ViewSet):
def list_11(self,request,*args,**kwargs): # 方法名随意和url对上就行
pass
def create(self,request,*args,**kwargs):
pass
def retrieve(self,request,*args,**kwargs):
pass
def update(self,request,*args,**kwargs):
pass
def partial_update(self,request,*args,**kwargs):
pass
def destroy(self,request,*args,**kwargs):
pass
(由来) B 2 ViewSet(B1 和 APIVIew 结合)
class ViewSet(ViewSetMixin, views.APIView
):
可以实现
:这两个一组合 就有了ViewSet , 可以使用APIView的解析器和渲染器了, 又能配置url- 但还需要手动定义 list create 等方法,
没怎么用到了,因为B3都比它好
(由来) B 3 GenericViewSet(B1 和GenericAPIView结合)
class GenericViewSet(ViewSetMixin, generics.GenericAPIView
):
可以实现更多
:它拥有GenericAPIView 搜索、排序、分页功能,并需要 给querySet 和 serializer_class- 但还需要手动定义 list create 等方法,
(最终) B4 ModelViewSet ( B3 和 mixin.py 文件结合)
可以实现
:利用Mixin提供的方法,就不需要手动去定义 list create方法了
A: 两个接口的,
实际上是 RetrieveModelMixin 里的 已经有了 retrieve()ListModelMixin里,已经有了 list()
class ReadOnlyModelViewSet(
mixins.RetrieveModelMixin,
mixins.ListModelMixin,
GenericViewSet):
B.六个接口的
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
C. *个接口(灵活使用)
假加,只想要 列表接口,更新接口, 把ModelViewSet超进来,保留想要的也行
from rest_framework import viewsets
class ProjectViewSet(mixins.ListModelMixin,
mixins.UpdateModelMixin,
viewsets.GenericViewSet):
queryset = Projects.objects.all()
serializer_class = ProjectsModelSerializer
继承ModelViewSet
的,代码基本上是这样写的
最后的代码:
from .models import Projects,Interfaces
from .serializers import ProjectsModelSerializer
from rest_framework import status,filters
from utils.pagination import PageNumberPagination
from rest_framework.viewsets import ModelViewSet
class ProjectViewSet(ModelViewSet):
queryset = Projects.objects.all()
serializer_class = ProjectsModelSerializer
# 搜索、排序功能
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
search_fields = ["name", 'leader']
ordering_fields = ['id', 'name', 'leader']
# 分页功能
pagination_class = PageNumberPagination
自定action(自定义接口)
除了以上的继承了mixins的几个类,提供的list等方法, 还可以自己定义action (方法) ,完成特定的需求。
情况1. 同list,不要需要传id的情况 如: projects/name ,
返回中,项目列表中每一项 只有 id 和name
情况2. 同查询,需要传id的。
使用get_object 就能知道传的id是啥。路由中就要定义id
如图,请求时路由里有个id。
也可以写的简单一点,如下
路由器对象,可以快速定义
作用:快速定义视图集的路由(可以自动生成路由条目,针对模型的增删改查)
上面的红框,可以使用如下代码代替
from rest_framework import routers
# 1.创建
# router = routers.SimpleRouter()
router = routers.DefaultRouter() # 相比使用SimpleRouter,它的根路径可以看见
# 2.注册,指定路由前缀, 指定视图集类
router.register(r'projects',views.ProjectViewSet)
urlpatterns = []
urlpatterns +=router.urls
以上代码的作用实际上是 设置了一个 路由前缀
, 去 匹配一个 模型视图集类
。 如继承的是ModelViewSet。
那么就有6个接口, 如下:
自定义action,还是自己处理
但是names,路由,和 /interfaces/ 路由,需要
方式1 :还是自己定义
from rest_framework import routers
# 1.创建
# router = routers.SimpleRouter()
router = routers.DefaultRouter() # 相比使用SimpleRouter,它的根路径可以看见
# 2.注册,指定路由前缀, 指定视图集类
router.register(r'projects',views.ProjectViewSet)
urlpatterns = [
path('projects/names/',views.ProjectViewSet.as_view({
'get':'names'
})),
path('projects/interfaces/<int:tk>/', views.ProjectViewSet.as_view({
'get': 'interfaces'
}))
]
urlpatterns +=router.urls
补充:如果是DefaultRouter,而不是SimpleRouter,根路径就是可以访问的
方式2:定义方法时加装饰器
from rest_framework.decorators import action
- detal 必须要指定,表示是否要传pk值
- methods,表示请求方法,默认为get
- url_path为 路由,默认为“方法名小写”
(注:如果detail为True, pk是在前面,url_path是在后面的)