zoukankan      html  css  js  c++  java
  • django-rest-framework

    源码繁琐,多说无益,耐心细读官方文档:

    https://www.django-rest-framework.org/

    个人总结:

    REST是一种软件架构设计风格,不是标准,也不是具体的技术实现,只是提供了一组设计原则和约束条件。

    DRF(Django RestFramework)是一套基于Django开发的、帮助我们更好的设计符合REST规范的Web应用的一个Django App,所以,本质上,它是一个Django App。

    安装: (确定Django已经安装)

       >>> pip install djangorestframework

    1 APIView 

      首先需要了解django中views.View类及其相关流程,看如下关系图(最好看源码):

      DRF APIView请求流程:

      DRF对django视图配置流程图(个人画)

    2 解析器组件 (用来解析数据的请求的组件)

      Django并不能处理请求协议为application/json编码协议的数据

      注意: DRF解析器会封装到View中的parsers内,在视图函数被调用时,会传入request,通过request.data拿到数据才进行解析 ,即解析器解析是在request对象传入后.

      解析器组件流程图:

    //解析器的使用方式:
    
        //1,导入模块 views.py
            from rest_framwork.views import APIView
        
        //2, 继承APIView
            class BookView(APIView):
                def get(self, request):
                    pass
    
        //3, url.py
            from django.urls import path, include, re_path
            from classbasedview import views
            urlpatterns = [
                re_path('login/$', views.LoginView.as_view()),
            ]
    
    
        //4, def post(self, request):
                origin_data = request.data
                    ...
                    return HttpResponse({})
                       

      试用工具: postman---通过postman来模拟用户请求,不再需要使用浏览器来发送请求.(直接在官网下载即可)


    3 序列化组件 

      序列化组件的使用:

      --get接口设计:

    • 导入序列化组件:from rest_framework import serializers
    • 定义序列化类,继承serializers.Serializer(建议单独创建一个专用的模块用来存放所有的序列化类):class BookSerializer(serializers.Serializer):pass
    • 定义需要返回的字段(字段类型可以与model中的类型不一致,参数也可以调整),字段名称必须与model中的一致
    • 在GET接口逻辑中,获取QuerySet
    • 开始序列化:将QuerySet作业第一个参数传给序列化类,many默认为False,如果返回的数据是一个列表嵌套字典的多个对象集合,需要改为many=True
    • 返回:将序列化对象的data属性返回即可

      {{ 实践代码 }}

      --post接口设计

    • url定义:需要为post新增url,因为根据规范,url定位资源,http请求方式定义用户行为
    • 定义post方法:在视图类中定义post方法
    • 开始序列化:通过我们上面定义的序列化类,创建一个序列化对象,传入参数data=request.data(application/json)数据
    • 校验数据:通过实例对象的is_valid()方法,对请求数据的合法性进行校验
    • 保存数据:调用save()方法,将数据插入数据库
    • 插入数据到多对多关系表:如果有多对多字段,手动插入数据到多对多关系表
    • 返回:将插入的对象返回

      {{ 实践代码 }}

      使数据自动插入而且更加简单:

    class BookSerializer(serializers.ModelSerializer):
        class Meta:
            model = Book
    
            fields = ('title',
                      'price',
                      'publish',
                      'authors',
                      'author_list',
                      'publish_name',
                      'publish_city'
                      )
            extra_kwargs = {
                'publish': {'write_only': True},
                'authors': {'write_only': True}
            }
    
        publish_name = serializers.CharField(max_length=32, read_only=True, source='publish.name')
        publish_city = serializers.CharField(max_length=32, read_only=True, source='publish.city')
    
        author_list = serializers.SerializerMethodField()
    
        def get_author_list(self, book_obj):
            # 拿到queryset开始循环 [{}, {}, {}, {}]
            authors = list()
    
            for author in book_obj.authors.all():
                authors.append(author.name)
    
            return authors

      步骤如下:

        继承ModelSerializer: 不再继承Serializer

        添加extra_kwargs类变量: extra_kwargs = { 'publish':{'write_only':True}}

    4 视图组件

      使用视图组件进行接口逻辑化

       导入mixin

    from rest_framework.mixinx import (
                        ListModelMix,
                        CreateModelMixin,
                        DestroyModelMixin,
                        UpdateModelMixin,
                        RetrieveModelMixin       
                                      )
    from rest_framework.generics import GenericAPIView
                 

        定义序列化类

    Class BookSerializer(serializers.ModelSerializer):
      class Meta:
        Book
          fields = ()
          extra_kwargs = {"field_name": {"write_only": True}}

        导入序列化类

        from .app_serializers import BookSerializer

      定义视图类

                class BookView(ListModelMix, CreateModelMixin, GenericAPIView):
                    # queryset和serializer_class是固定的写法
                    queryset = Book.objects.all()
                    serializer_class = BookSerializer
                  
                    def get():
                        return self.list()
                    
                    def post():
                        return self.create()
                        
                class BookFilterView(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView):
                    queryset = Book.objects.all()
                    serializer_class = BookSerializer
                    
                    def get():
                        return self.retrieve()
                        
                    def delete():
                        return self.destroy()
                        
                    def put():
                        return self.update()

       注意: 单条数据操作的url是这样的:re_path(r'books/(?P<pk>d+)/$, views.BookFilterView.as_view())

      使用视图组件的view进行接口逻辑优化

        导入模块  from rest_framework import generics

        写试图类

    class BookView(generics.ListCreateAPIView)
                        queryset = Book.objects.all()
                        serializer_class = BookSerializer
                        
                    class BookFilterView(generics.RetrieveUpdateDestroyAPIView):
                        queryset = Book.objects.all()
                        serializer_class = BookSerializer

      使用视图组件的viewset进行接口逻辑优化

        导入模块 from rest_framework.viewset import ModelViewSet

        设计url

    re_path(r'books/$, views.BookView.as_view({
                        'get': 'list',
                        'post': 'create'
                    })),
                    re_path(r'books/(?P<pk>d+)/$', views.BookView.as_view({
                        'get': 'retrieve',
                        'delete': 'destroy',
                        'put': 'update'
                    }))

        设计视图类

           class BookView(ModelViewSet):
               queryset = Book.objects.all()
               serializer_class = BookSerializer
    • Django程序启动,开始初始化,获取配置信息,获取视图类并加载到内存中,获取url及视图类的对应关系
    • 开始绑定视图类和url的对应关系,执行as_view()方法
    • as_view()方法被执行的时候传递了参数,为字典形式:{ “get”: “retrieve”, “delete”: “destroy”, “put”: “update” }
    • 上一步中执行as_view()方法传递参数的目的是为了完成优化,将delete请求方式重新命名为不同的函数
    • ViewSetMixin类重写了as_view()方法,也就是在这个地方将几个函数重新绑定,它并没有重写dispatch方法
    • 该方法返回视图函数view,注意在这个函数中有一个行 self = cls(**initkwargs), cls是视图类,执行视图函数时self就指向视图函数的实例对象
    • 等待客户端请求
    • 请求到来,开始执行视图函数,注意,调用视图函数时的方式是view(request),而如果url带有参数,调用方式为view(request, xxx=id)的形式
    • 显然,我们有命名参数(?Pd+),所以此时的调用方式为view(request, pk=id)
    • 视图函数中有一行self.kwargs = kwargs,所以pk已经被视图函数找到了
    • 视图函数返回self.dispatch(),开始执行dispatch方法,注意self是视图类的实例化对象(每个请求都被封装为一个对象)
    • dispatch开始执行get方法,注意此时的get方法会执行retrieve,以为已经被重定向了
    • 开始执行retrieve,有一行instance = self.get_object(), 该方法在GenericAPIView中
    • 至关重要的是拿到self.kwargs中的pk关键字,然后从queryset中拿到想要的数据
    • 返回结果

    5 认证组件

      cookie和session两种方式可以保存用户信息,这两种方式不同的是cookie保存在客户端浏览器中,而session保存在服务器中,他们各有优缺点,配合起来使用,可将重要的敏感的信息存储在session中,而在cookie中可以存储不太敏感的数据。

      token称之为令牌。cookie、session和token都有其应用场景,没有谁好谁坏,不过开发数据接口类的Web应用,目前用token还是比较多的。

      token认证步骤:

        用户登录,服务器端获取密码,查询用户表,如果存在该用户且第一次登录(或者token过期), 生成token,否则返回错误信息

        如果用户不是第一次登录,且token未过期,更新token值

      创建俩个model,(token可以存储在user表中,建议存储在user表中):

        

    from django.db import models
    
    # Create your models here.
    
    class User(models.Model):
        username = models.CharField(max_length=32)
        password = models.CharField(max_length=32)
        user_type_entry = (
            (1, 'Delux'),
            (2, 'SVIP'),
            (3, "VVIP")
        )
        user_type = models.IntegerField(choices=user_type_entry)
        address = models.CharField(max_length=32)
    
        def __str__(self):
            return self.username
    
    class UserToken(models.Model):
        user = models.OneToOneField("User", on_delete=models.CASCADE)
        token = models.CharField(max_length=128)

      因为涉及登录认证,所以写post方法接口,登录都是post请求:

      

    from django.http import JsonResponse
    
    from rest_framework.views import APIView
    
    from .models import User, Book, UserToken
    from .utils import get_token
    
    
    class UserView(APIView):
    
        def post(self, request):
            response = dict()
            try:
                username = request.data['username']
                password = request.data['password']
    
                user_instance = User.objects.filter(
                    user_name=username,
                    password=password
                ).first()
    
                if user_instance:
                    access_token = get_token.generater_token()
    
                    UserToken.objects.update_or_create(user=user_instance, defaults={
                        "token": access_token
                    })
                    response["status_code"] = 200
                    response["status_message"] = "登录成功"
                    response["access_token"] = access_token
                    response["user_role"] = user_instance.get_user_type_display()
                else:
                    response["status_code"] = 201
                    response["status_message"] = "登录失败,用户名或密码错误"
            except Exception as e:
                response["status_code"] = 202
                response["status_message"] = str(e)
    
            return JsonResponse(response)

      通过获取随机字符串的方法用来生成token值:

    # -*- coding: utf-8 -*-
    import uuid
    
    
    def generater_token():
        random_str = ''.join(str(uuid.uuid4()).split('-'))
        return random_str

      DRF认证组件的使用:

        新建一个认证类,包含之后的认证逻辑:

    class UserAuth(object):
    
        def authenticate_header(self, request):
            pass
    
        def authenticate(self, request):
            user_post_token = request.query_params.get('token')
    
            token_object = UserToken.objects.filter(token=user_post_token).first()
            if token_object:
                return token_object.user.username, token_object.token
            else:
                raise APIException("认证失败")

      实现方式看上去非常简单,到token表里面查看token是否存在,然后根据这个信息,返回对应信息即可,然后,在需要认证通过才能访问的数据接口里面注册认证类即可: 

    class BookView(ModelViewSet):
    
        authentication_classes = [UserAuth, UserAuth2]
    
        queryset = Book.objects.all()
        serializer_class =  BookSerializer

      多个认证类实现:

        注意:若需要返回数据,请在最后一个认证类中返回,因为在前面返回,self.authentication()方法中会对返回值进行判断,若不为空,认证的过程就会终止.  多个认证类实现方式如下:

    class UserAuth2(object):
    
        def authenticate(self, request):
            raise APIException("认证失败")
    
    
    class UserAuth(object):
    
        def authenticate_header(self, request):
            pass
    
        def authenticate(self, request):
            user_post_token = request.query_params.get('token')
    
            token_object = UserToken.objects.filter(token=user_post_token).first()
            if token_object:
                return token_object.user.username, token_object.token
            else:
                raise APIException("认证失败")
    
    
    class BookView(ModelViewSet):
    
        authentication_classes = [UserAuth, UserAuth2]

      简化authenticate_header方法,如下:(继承BaseAuthentication类即可)

    from rest_framework.authentication import BaseAuthentication
    
    class UserAuth2(BaseAuthentication):
    
        def authenticate(self, request):
            raise APIException("认证失败")
    
    
    class UserAuth(BaseAuthentication):
    
        def authenticate(self, request):
            user_post_token = request.query_params.get('token')
    
            token_object = UserToken.objects.filter(token=user_post_token).first()
            if token_object:
                return token_object.user.user_name, token_object.token
            else:
                raise APIException("认证失败")

      全局认证: 

        实现所有的数据接口都需要认证:  

    authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES

      如果认证类自己没有authentication_classes,就会到settings中去找,通过这个机制,我们可以将认证类写入到settings文件中即可实现全局认证:

    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'authenticator.utils.authentication.UserAuth',
            'authenticator.utils.authentication.UserAuth2',
        ),
    }

    6 权限组件 

      定义权限类: 

    class UserPerms():
        message = "您没有权限访问该数据"
        def has_permission(self, request, view):
            if request.user.user_type > 2:
                return True
            else:
                return False

      同样的逻辑,同样的方式,只是执行权限的方法名与执行认证的方法名不一样而已,名为has_permission,并且需要将当前的视图类传递给该方法。

      视图类中加入permission_classes变量:

    class BookView(ModelViewSet):
    
        authentication_classes = [UserAuth]
        permission_classes = [UserPerms2]
    
        queryset = Book.objects.all()
        serializer_class =  BookSerializer

    7 频率组件


    8 url控制器组件
    9 分页器组件
    10 响应器组件

  • 相关阅读:
    Javascript动画模拟
    C#导出Excel
    Google Maps API
    动态管理视图和函数
    HttpWebRequest和HttpWebResponse实例
    从零开始学Java 第19章 网络编程
    从零开始学Java 第15章 Java输入输出流
    从零开始学Java 第21章 集合框架
    从零开始学Java 第13章 多线程
    从零开始学Java 第14章 Applet程序
  • 原文地址:https://www.cnblogs.com/panda-pandeyong/p/10100459.html
Copyright © 2011-2022 走看看