day73:drf:drf视图相关类&路由Routers&创建虚拟环境
目录
3.5个视图扩展类:ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
4.GenericAPIView的视图子类:ListAPIView,CreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView
5.视图集基类:ViewSet,GenericViewSet,ModelViewSet,ReadOnlyViewSet
3.DefaultRouter和SimpleRouter的区别(了解)
APIView
APIView总结
1.APIView中封装了自己的request和response
2.django:request.GET对应drf中的request.query_params
3.django:request.POST对应drf中的request.data
4.APIView中的response不需要写参数safe=False,ensure_ascii这些东西了,因为这些功能已经封装在response里面了
5.当我们在浏览器上访问一个url,给你回复的是一个界面。如果我们用postman访问url,给你返回的就是json数据。这是因为response中有相关配置导致的。
APIView实现代码
class Students1View(APIView): # 获取所有数据接口 def get(self,request): all_data = models.Student.objects.all() # 获取数据库中Student表中所有数据 serializer = StudentSerializer(instance=all_data,many=True) # 将后端数据序列化 return Response(serializer.data) # 将序列化之后的数据传递给前端 # 添加一条记录的接口 def post(self,request): data = request.data # 获取用户在前端输入的数据 serializer = StudentSerializer(data=data) # 将数据反序列化 if serializer.is_valid(): # 校验 instance = serializer.save() # instance为添加的新纪录对象 serializer = StudentSerializer(instance=instance) # 根据API规范,数据保存之后应该显示到前端上,所以需要将新添加的数据序列化 return Response(serializer.data,status=status.HTTP_201_CREATED) else: print(serializer.errors) class Student1View(APIView): # 获取单条记录 def get(self,request,pk): stu_obj = models.Student.objects.get(pk=pk) serializer = StudentSerializer(instance=stu_obj) return Response(serializer.data) # 更新单条记录 def put(self,request,pk): stu_obj = models.Student.objects.get(pk=pk) data = request.data serializer = StudentSerializer(instance=stu_obj, data=data, partial=True) if serializer.is_valid(): instance = serializer.save() # instance为添加的新纪录对象 serializer = StudentSerializer(instance=instance) return Response(serializer.data) else: print(serializer.errors) # 删除单条记录 def delete(self,request,pk): models.Student.objects.get(pk=pk).delete() return Response(None,status=status.HTTP_204_NO_CONTENT)
GenericAPIView:通用视图类
GenerivAPIView继承自APIView方法,
from rest_framework.generics import GenericAPIView class Students2View(GenericAPIView,): queryset = models.Student.objects.all() # 指明要使用的数据查询集[必须指定] serializer_class = StudentSerializer # 指明视图要使用的序列化器[可写可不写] # 通过get_serializer_class来控制不同条件下,使用不同的序列化器类 def get_serializer_class(self): if self.request.method == 'GET': return Student2Serializer else: return StudentSerializer # 获取所有数据接口 def get(self, request): # all_data = models.Student.objects.all() # serializer = StudentSerializer(instance=all_data, many=True) serializer = self.get_serializer(instance=self.get_queryset(), many=True) return Response(serializer.data) # 添加一条记录的接口 def post(self, request): data = request.data serializer = self.get_serializer(data=data) if serializer.is_valid(): instance = serializer.save() # instance为添加的新纪录对象 serializer = self.get_serializer(instance=instance) return Response(serializer.data, status=status.HTTP_201_CREATED) else: print(serializer.errors) return Response({'error':'字段错误'}) class Student2View(GenericAPIView): queryset = models.Student.objects.all() serializer_class = StudentSerializer # 获取单条记录 def get(self, request, pk): # stu_obj = models.Student.objects.get(pk=pk) serializer = self.get_serializer(instance=self.get_object()) return Response(serializer.data) # 更新单条记录 def put(self, request, pk): # stu_obj = models.Student.objects.get(pk=pk) data = request.data serializer = self.get_serializer(instance=self.get_object(), data=data, partial=True) if serializer.is_valid(): # print('>>>',serializer.data) #在save方法之前,不能调用data属性,serializer.data instance = serializer.save() # instance为添加的新纪录对象 print(serializer.data) #之后可以看 serializer = self.get_serializer(instance=instance) return Response(serializer.data) else: print(serializer.errors) # 删除单条记录 def delete(self, request, pk): models.Student.objects.get(pk=pk).delete() return Response(None, status=status.HTTP_204_NO_CONTENT)
GenericAPIView中的属性和方法
在视图中可以调用该方法获取详情信息的模型类对象。
返回序列化器类,默认返回
5个视图扩展类:????ModelMixin
这五个扩展类需要搭配GenericAPIView父类,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法。
换句话说。就是你在视图中的get post put等这些方法中不用写里面的代码了,里面的代码相关的操作已经被封装到对应的Mixin中了。
class Students3View(GenericAPIView,ListModelMixin,CreateModelMixin): queryset = models.Student.objects.all() serializer_class = StudentSerializer # 获取所有数据接口 def get(self, request): return self.list(request) # 添加一条记录的接口 def post(self, request): return self.create(request) class Student3View(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin): queryset = models.Student.objects.all() serializer_class = StudentSerializer # 获取单条记录 def get(self, request, pk): return self.retrieve(request, pk) # 更新单条记录 def put(self, request, pk): return self.update(request, pk) # 删除单条记录 def delete(self, request, pk): return self.destroy(request, pk)
GenericAPIView的视图子类:????APIView
上面的代码还是过于麻烦,因为既要继承GenericAPIView又要继承Mixin系列的类。
所以将各自的操作封装成自己的APIView类。用哪个继承哪个。
而且连函数都不用写了,在???APIView类中已经有了,所以不用再写了,继承自己的APIView类即可。
from rest_framework.generics import ListAPIView,CreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView class Students4View(ListAPIView,CreateAPIView): queryset = models.Student.objects.all() serializer_class = StudentSerializer class Student4View(RetrieveAPIView,UpdateAPIView,DestroyAPIView): queryset = models.Student.objects.all() serializer_class = StudentSerializer
视图集基类:ViewSet,GenericViewSet,ModelViewSet,ReadOnlyViewSet
ViewSet
主要解决问题:之前我们在写视图类的时候,获取多条数据和提交数据放在了一个类中。获取单条数据,更新单条数据,删除多条数据放到了一个类中
这是因为多条数据操作时不需要指定pk值,而针对单条数据时,需要指定pk值。也就是说需要知道你要操作哪一条数据
这样我们就很矛盾。我们没法将5个方法放到同一个类中。
而下面的ViewSet就可以解决这个问题,将5个方法放到同一个类中
from rest_framework.viewsets import ViewSet class Students5View(ViewSet): # 获取所有数据接口 def get_all_student(self,request): # action all_data = models.Student.objects.all() serializer = StudentSerializer(instance=all_data,many=True) return Response(serializer.data) # 添加一条记录的接口 def add_student(self,request): data = request.data serializer = StudentSerializer(data=data) if serializer.is_valid(): instance = serializer.save() #instance为添加的新纪录对象 serializer = StudentSerializer(instance=instance) return Response(serializer.data,status=status.HTTP_201_CREATED) else: print(serializer.errors) def get_one(self,request,pk): stu_obj = models.Student.objects.get(pk=pk) serializer = StudentSerializer(instance=stu_obj) return Response(serializer.data)
通过上面代码我们可以看出。单条数据操作和多条数据被放到了一个类中。并且方法名也不再局限于必须要使用get post put...这些方法名了。
这是因为在urls.py中的as_view方法做了一个请求方法和对应函数名的指定。
而获取单条数据和获取多条数据能够共存在一个类中,是因为他们处于两个url中。虽然都是get请求过去的,但是他们在不同的url中,会执行各自的视图方法
通过下面url,我们就可以发挥出ViewSet的用处了:
path('students5/', views.Students5View.as_view({'get':'get_all_student','post':'add_student'})), re_path('students5/(?P<pk>\d+)/', views.Students5View.as_view({'get':'get_one'})),
ViewSet+????APIView
用我们刚才学的ViewSet和之前的???APIView做一个结合~
效果:即放到了一个类中,而且各个方法里不用写详细代码(????APIView的作用)
"""发挥下ViewSet的作用""" from rest_framework.viewsets import ViewSet from rest_framework.generics import ListAPIView,CreateAPIView,RetrieveAPIView class Student2ViewSet(ViewSet,ListAPIView,CreateAPIView,RetrieveAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer
def get_all(self,request): return self.list(request) def add_student(self,request): return self.create(request) def get_one(self,request,pk): return self.retrieve(request)
GenericViewSet+???Mixin
效果和Viewset+???APIView是相同的
from rest_framework.viewsets import GenericViewSet from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin class Student3ViewSet(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin): queryset = Student.objects.all() serializer_class = StudentModelSerializer def get_all(self,request): return self.list(request) def add_student(self,request): return self.create(request) def get_one(self,request,pk): return self.retrieve(request)
现在我们的代码是这个样子的。美中不足是我们还需要自己写def方法,其实我们希望的是类里面没有def方法的
其实。这个需求刚才在前面已经提到了。
只需要在urls.py中将get post put这类请求类型直接与Mixin中内置的list,create,update等动作方法关联起来。
这样它就会自动去执行里面的list,create,update方法。不需要你在视图里定义一个函数名称然后再return self.create()了。
urls.py如下所示:
urlpatterns = [ path("students7/", views.Student4ViewSet.as_view({"get": "list", "post": "create"})), re_path("students7/(?P<pk>\d+)/", views.Student4ViewSet.as_view({"get": "retrieve","put":"update","delete":"destroy"})), ]
url直接就是{请求类型:action动作名},这样我们在视图那一块的代码又可以缩减了。函数全部无需定义了。
经过改良后,视图代码如下所示:
from rest_framework.viewsets import GenericViewSet from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin class Student4ViewSet(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin): queryset = Student.objects.all() serializer_class = StudentModelSerializer
ModelViewSet
上面的代码,在导入和继承的时候太麻烦了,需要写5个Mixin类
这时出现了ModelViewSet,ModelViewSet同时继承ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin这五个类。
这样写起来就更加简单了。
终极版如下所示:
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet from rest_framework.decorators import action class StudentModelViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer
ReadOnlyViewset
举例,比如做一个登录方法login:
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet class StudentModelViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer def login(self,request): # 这个就可以称为自定义的action动作 """学生登录功能""" return Response({"message":"登录成功"})
urls.py这样写
urlpatterns = [ path("students8/", views.StudentModelViewSet.as_view({"get": "list", "post": "create"})), re_path("students8/(?P<pk>\d+)/", views.StudentModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})), path("stu/login/",views.StudentModelViewSet.as_view({"get":"login"})) ]
在视图集中,我们可以通过action对象属性来获取当前请求视图集时的action动作是哪个。
from rest_framework.viewsets import ModelViewSet from students.models import Student from .serializers import StudentModelSerializer from rest_framework.response import Response class StudentModelViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer def get_new_5(self,request): """获取最近添加的5个学生信息""" # 操作数据库 print(self.action) # 获取本次请求的视图方法名 '''通过路由访问到当前方法中.可以看到本次的action就是请求的方法名'''
路由:Routers
如何添加路由数据
如何添加路由数据,并且和视图函数做关联:
from django.urls import path, re_path from . import views urlpatterns = [ ... ] """使用drf提供路由类router给视图集生成路由列表""" # 实例化路由类 # drf提供一共提供了两个路由类给我们使用,他们用法一致,功能几乎一样 from rest_framework.routers import DefaultRouter router = DefaultRouter() # 注册视图集 # router.register("路由前缀",视图集类) router.register("router_stu",views.StudentModelViewSet) # 把生成的路由列表追加到urlpatterns urlpatterns += router.urls
所以我们如果也要给自定义方法生成路由,则需要进行action动作的声明。
在视图集中附加action声明
以action装饰器装饰的方法名会作为action动作名,与list、retrieve等同。
action装饰器可以接收两个参数:
-
methods: 声明该action对应的请求方式,列表传递
-
detail:
声明该action的路径是否与单一资源对应,及是否是
xxx/<pk>/action方法名/
-
True 表示路径格式是
xxx/<pk>/action方法名/
-
False 表示路径格式是
-
from rest_framework.viewsets import ModelViewSet from rest_framework.decorators import action class StudentModelViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer # methods 设置当前方法允许哪些http请求访问当前视图方法 '''detail 设置当前视图方法是否是操作一个数据''' # detail为True,表示路径名格式应该为 router_stu/{pk}/login/ @action(methods=['get'], detail=True) def login(self, request,pk): """登录""" ... # detail为False 表示路径名格式应该为 router_stu/get_new_5/ @action(methods=['put'], detail=False) def get_new_5(self, request): """获取最新添加的5个学生信息""" ...
DefaultRouter和SimpleRouter的区别(了解)
创建虚拟环境
1.安装虚拟环境
# 1.管理员方式打开终端 # 2.安装虚拟环境 pip install virtualenv -i https://pypi.douban.com/simple # 3.终端关闭,再重新打开 # 4.注意:创建[环境名称]文件夹,放置所有的环境,进入指定目录 D:/envs # 5.创建虚拟环境 virtualenv 环境名称 'C:\python\python3.6.exe' '''这样写的前提是你的电脑已经将python加入到环境变量中''' virtualenv 环境名称 --python=python3.6
2.激活虚拟环境
# 1.进入到虚拟环境文件夹下的Scripts目录 activate '''激活虚拟环境''' # 2.退出虚拟环境 deactivate '''退出虚拟环境'''
3.在虚拟环境中安装模块
# 1.激活虚拟环境 # 2.在激活的虚拟环境中安装模块 pip3 install django==2.2 -i http://pypi.douban.com/simple --trusted-host pypi.douban.com # 3.搭建项目环境,在pycharm选择解释器时,选择你之前已经创建好的虚拟环境
4.在pycharm上使用虚拟环境
附:APIView思维导图
附:drf的执行流程图