day73:drf:drf视图相关类&路由Routers&创建虚拟环境

目录

1.APIView

2.GenericAPIView:通用视图类

3.5个视图扩展类:ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin

4.GenericAPIView的视图子类:ListAPIView,CreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView

5.视图集基类:ViewSet,GenericViewSet,ModelViewSet,ReadOnlyViewSet

  1.ViewSet

  2.ViewSet+????APIView

  3.GenericViewSet+???Mixin

  4.ModelViewSet

  5.ReadOnlyViewset

6.路由:Routers

  1.如何添加路由数据

  2.在视图集中附加action声明

  3.DefaultRouter和SimpleRouter的区别(了解)

7.创建虚拟环境

附:APIView思维导图

附:drf的执行流程图

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中的属性和方法

serializer_class 指明视图使用的序列化器

get_serializer(self, *args, **kwargs) 返回序列化器对象,主要用来提供给Mixin扩展类使用,如果我们在视图中想要获取序列化器对象,也可以直接调用此方法

queryset 指明使用的数据查询集

 

get_queryset(self) 返回视图使用的查询集,主要用来提供给Mixin扩展类使用,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写

get_object(self) 返回详情视图所需的模型类数据对象,主要用来提供给Mixin扩展类使用。在视图中可以调用该方法获取详情信息的模型类对象。

get_serializer_class(self) 当出现一个视图类中调用多个序列化器时,那么可以通过条件判断在get_serializer_class方法中通过返回不同的序列化器类名就可以让视图方法执行不同的序列化器对象了

            返回序列化器类,默认返回serializer_class

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

继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。

视图集中定义附加action动作

在视图集中,除了上述默认的方法动作外,还可以添加自定义动作,进行扩展。

举例,比如做一个登录方法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声明

在视图集中,如果想要让Router自动帮助我们为自定义的动作生成路由信息,需要使用rest_framework.decorators.action装饰器

以action装饰器装饰的方法名会作为action动作名,与list、retrieve等同。

action装饰器可以接收两个参数:

  • methods: 声明该action对应的请求方式,列表传递

  • detail:

      声明该action的路径是否与单一资源对应,及是否是

    xxx/<pk>/action方法名/
    • True 表示路径格式是xxx/<pk>/action方法名/

    • False 表示路径格式是xxx/action方法名/

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的区别(了解)

DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。

比如使用DefaultRouter时你访问一下http://127.0.0.1:8001/router_stu/ 会看到一个页面。

而SimpleRouter会报错,一般都需要有个查看所有接口的页面,所以我们基本都是用的是DefaultRouter

创建虚拟环境

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的执行流程图

 

posted @ 2020-10-22 22:56  iR-Poke  阅读(275)  评论(0编辑  收藏  举报