• CBV源码分析+APIVIew源码分析


    django的请求生命周期

    {drf,resful,apiview,序列化组件,视图组件,认证组件,权限组件,频率组件,解析器,分页器,响应器,URL控制器,版本控制}


    一、CBV源码分析
    准备工作:
    新建一个Django项目
    写一个基于类的视图


    继承,写get,post方法

    from django.shortcuts import render, HttpResponse
    
    from django.views import View
    from django.http import JsonResponse
    
    
    # Create your views here.
    
    
    class Test(View):
    
        def dispatch(self, request, *args, **kwargs):
            # codes
            obj = super().dispatch(self, request, *args, **kwargs)
            # codes
            return obj
    
        def get(self, request, *args, **kwargs):
            return HttpResponse('cbv_get')
    
        def post(self, request, *args, **kwargs):
            return HttpResponse('cbv_post')


    视图写好之后写路由
    把views导过来,

    类名.as_view()  直接执行

    from django.conf.urls import url
    from django.contrib import admin
    from appp01 import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        # 在这里写的 views.Test.as_view(),实际上就是as_view()里面view的内存地址
        url(r'^test/', views.Test.as_view()),
    
    ]

    分析一下执行的流程:
      如果当初放的是函数内存地址不会去执行,请求来了经过路由做分发。但是如果放的是函数加括号直接就执行了(程序一点一运行立马这里就执行),执行Test.as_view的方法,

      Test里面没有as_view,所以执行的是父类View里面的as_view方法

    打开父类的View找到as_view方法,类来调用就把类传入,定义了一个view函数,最后返回了这个view,所以在路由层写的实际上就是view(as_view()内部的)这个函数的内存地址。

    当请求来了,来了执行的实际上就是view加括号执行。在执行前

    这里面外层函数有个cls(这个cls就是Test这个类,产生了对象赋值给了self。),在内部函数有个对外部作用域的引用,所以这个view就是一个闭包函数。

    把请求的request对象赋到了self.request里面

    最后返回了一个self.dispatch函数,其实就是在执行这个函数。

    这个dispatch函数就是生成的Test这个类的,这个Test类里面没有这个dispatch,所以执行的是View的dispatch。

    所以请求来了就是执行dispatch方法

    这里面取到请求的小写,判断在不在self.http_method_names中,如果是get请求在这个列表里面

    通过getattr反射,self是Test对象,找到get方法,找不到返回错误提示(

    self.http_method_not_allowed

    handler=test对象中get方法的内存地址,

    两层判断。

    看self.http_method_names源码

    self.http_method_not_allowed源码

    源码执行的流程?
    CBV源码分析
    cbv和fbv
    1 在views中写一个类,继承View,里面写get方法,post方法
    2 在路由中配置: url(r'^test/', views.Test.as_view()),实际上第二个参数位置,放的还是一个函数内存地址
    3 当请求来了,就会执行第二个参数(request,参数),本质上执行view()
    4 view内部调用了dispatch()方法
    5 dispatch分发方法,根据请求方式不同,执行的方法不同

    二、resful规范  ----->软件开发的一种规范,好处是方便前台跟后台交互,后台跟后台交互可以直接访问
    面向资源架构,面向资源编程。把网络上所有东西都当成资源。


    10个规范
    1.与后台做交互,通常采用https协议,这样安全

    2.域名 有两种格式
    https://api.baidu.com 这就说明这是百度提供的这个接口,这种存在跨域问题。
    https://www.baidu.com.api/

    3.版本 ,接口不能保证不改动
    https://www.baidu.com.api/v1
    https://www.baidu.com.api/v2

    4.路径上所有的东西都是资源,均使用名词来表示。不用动词get,delet。。。
    https://www.library.com.api/v1/books/ 拿所有书
    https://www.library.com.api/v1/book/ 拿一本书

    5.method(请求的方式)来表示增删查改,链接上看不出来只有在请求方式上能看出来增删查改。
    https://www.library.com.api/v1/books/ get请求,获取所有书
    https://www.library.com.api/v1/books/ post请求,新增一本书
    https://www.library.com.api/v1/book/1 delete请求,删除一本id为1的书
    https://www.library.com.api/v1/book/1 get请求,获取id为1的这本书
    https://www.library.com.api/v1/book/1 put/patch请求,修改id为1的这本书

    6.过滤条件
    https://www.library.com.api/v1/books?limit=10 只拿前十本书
    https://www.library.com.api/v1/books?price=20 只拿价格为20元的书

    7.状态码
    200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
    201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
    202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
    204 NO CONTENT - [DELETE]:用户删除数据成功。
    400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
    401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
    403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
    404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
    406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
    410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
    422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
    500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
    等价于:{'status':100,}


    8.错误处理,返回错误信息,error当做key
    {status:101,error:"不可以这样"}

    9.返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。
    GET /collection:返回资源对象的列表(数组)
    GET /collection/resource:返回单个资源对象
    POST /collection:返回新生成的资源对象
    PUT /collection/resource:返回完整的资源对象
    PATCH /collection/resource:返回完整的资源对象
    DELETE /collection/resource:返回一个空文档

    10.返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
    返回结果中提供链接(获取一本书)
    {
    id:1
    name:xxx
    price:12
    publish:www.xxx.com.api/v1/publish/1

    }


    三、Django中写resful的接口
    路由设计很重要。

    urlpatterns = [
    url(r'^users/', views.users),
    url(r'^user/(?P<id>d+)', views.user),
    ]

    模拟数据库,列表嵌套字典

    users_list = [{"id":1,"name":'zrg',"age":18},{"id":2,"name":'yj',"age":19},{"id":3,"name":'xyw',"age":18}]

    def users(request):
        # 返回字典里面写status,如果成功的话。  需要把users_list取到的数据赋给response
        response = {'status': 100, 'erros': None}
        # 如果method等于get请求的话获取所有用户,并且返回resful规范
        if request.method == 'GET':
            response['users'] = users_list
            # 导入JsonResponse,字典里面套了列表所以safe=False
            return JsonResponse(response, safe=False)
        # 如果method等于post请求,新增
        if request.method == 'POST':
            # 新增的话,数据取出来添加到users_list里面去
            name = request.POST.get('name')
            age = request.POST.get('age')
            users_list.append({'id': len(users_list) + 1, 'name': name, 'age': age})
            # response['user']={'name':name,'age':age}
            response['msg'] = '新增成功'
            return JsonResponse(response)

    # 单个的增删改
    # 把参数传过来,通过分组来做(有名无名分组)

    # 单个的增删改
    # 把参数传过来,通过分组来做(有名无名分组)
    def user(request, id):
        response = {'status': 100, 'erros': None}
        if request.method == 'GET':
            id = int(id)
            response['user'] = users_list[id]
            return JsonResponse(response)

    用postman提交不会自动补/,在浏览器会自动补/(中间件的原理添加)。

    所以在postman这个软件里用就写全的路径!get请求可以重定向,post请求没有重定向这一说。

    四、drf写resful的接口   是一个app
    drf:Djangorestframework
    1.安装

    pip3 install Djangorestframework

    在pycharm里也可以安装

    2.简单使用

    要使用先去APP里注册

    基于drf写接口,写cbv,视图层# 基于drf写接口,写cbv
    # 没有也可以写,有了更好写接口
    # 导入rest_framework里面的views
    from rest_framework.views import APIView
    from rest_framework.response import Response  # Response 也是继承了HttpResponse,这个Response就传一个字典就可以了,会自动序列化。
    
    # 写类继承APIView,写get,post方法
    # class DrfTest(views.APIView):
    # 等同于
    class DrfTest(APIView):
        def get(self, request, *args, **kwargs):
            response = {'status': 100, 'erros': None}
            response['users'] = users_list
            # 传列表传字典
            # 用drf的Response,可以通过请求客户端来判断返回数据格式是什么样的,用浏览器返回的是页面。。。
            return Response(response)
    
        #   返回JsonResponse也可以,有数据,没页面
    
        def post(self, request, *args, **kwargs):
            name = request.data.get('name')
    pass
    # 原生Django只能处理form-data 和 urlencode 编码,json处理不了,drf就能处理了,但是取值从大写POST里面取值。
        def post(self, request, *args, **kwargs):
            # 传参
            # data方法这里面是所有编码方式都能转成字典
            name = request.data.get('name')  #从data这个字典里拿到post提交的form-data 和 urlencode ,json格式的数据
            print(name)
            return HttpResponse('ok')

    APIVIew源码分析
    dispatch方法:
    # 传入的request是原生的request对象
    # 这个request已经不是原生的request了,但是它内部有个原生的request对象

    request = self.initialize_request(request, *args, **kwargs)
    self.initial(request, *args, **kwargs)#这里面有权限,认证,频率


    Request源码的分析

    as_view,dispatch,self.initialize_request,里面Request

    request.data实际上是个方法,才转换数据(如果前端传的formdata,URLencode就从大写post返回,如果json从body体里拿出来转换回来。---->解析器)

    也有request.method,触发attr,反射原来的request

    request.POST,等价于request._request.POST

    request.GET  

    Request源码分析
    request.data其实是个方法,被包装成了属性
    重写了__getattr__

     print(request.GET)
    # 就相当于:
    print(request.query_params)   query_params----->查询的参数

    五、drf之序列化组件

      序列化:把Python中的对象转成json格式字符串

      反序列化:把json格式转换成我樱桃红对象

    序列化组件

    Serializer

    ModelSerialier

    class MEta:

      model

      fields

      depth

      publish=

    全局,局部钩子函数

    反序列化:

      保存

      修改

    全局跟局部钩子 看源码底往上走,自己-父类-父类的父类。。。。 self当前序列化的对象

     ----------------- ------------------- ------------------- -------------

    第二步 使用 books_xlh = BookXlh(books_l,many=True)

    books_xlh.data就是序列化完成的字典
    from django.shortcuts import render
    
    # Create your views here.
    from rest_framework.response import Response
    
    from rest_framework.views import APIView
    
    from app01 import models
    
    from app01.Myxlh import BookXlh
    
    
    # # 写一个获取所有图书的接口
    # class Books(APIView):
    #     def get(self,request,*args,**kwargs):
    #         # 所有图书拿出来,转成json格式
    #         books_l=models.Book.objects.all()
    #         # 用的话,要序列化谁,就写一个序列化类(新建文件夹或py文件),这个类继承一个东西,
    #         # 然后用的时候直接用这个类生成一个对象就可以了。
    #         # 比如序列化这个book,就单独为这个book写个序列化的类
    #
    #         # 用BookXlh需要导入
    #         # 第一个参数需要序列化的queryset对象,第二个参数many=True(如果序列化多条必须)
    #         books_xlh = BookXlh(books_l,many=True)
    #         # isinstance=单个对象的时候,many=False  比如 books_l=models.Book.objects.all().first
    #         print(books_xlh.data)   #是一个字典
    #         return Response(books_xlh.data)
    
    
    # 优化以上代码
    # 写一个获取所有图书的接口
    class Books(APIView):
    
        def get(self, request, *args, **kwargs):
            # 字典应该是:
            response = {'status': 100, 'msg': 'successful'}
            # 所有图书拿出来,转成json格式
            books_l = models.Book.objects.all()
            # 用的话,要序列化谁,就写一个序列化类(新建文件夹或py文件),这个类继承一个东西,
            # 然后用的时候直接用这个类生成一个对象就可以了。
            # 比如序列化这个book,就单独为这个book写个序列化的类
    
            # 用BookXlh需要导入
            # 第一个参数需要序列化的queryset对象,第二个参数many=True(如果序列化多条必须)
            books_xlh = BookXlh(books_l, many=True)
            # isinstance=单个对象的时候,many=False  比如 books_l=models.Book.objects.all().first
            print(books_xlh.data)  # 是一个字典
            response['books'] = books_xlh.data
            return Response(response)
    
    
    # 获取一本书的
    class Book(APIView):
        def get(self, request, id):
            response = {'status': 100, 'msg': '成功了'}
            book = models.Book.objects.filter(pk=id).first()
            book_xlh = BookXlh(book, many=False)
            response['book'] = book_xlh.data
            return Response(response)

    第一步先写一个类(要序列化哪个表模型,就对应着哪个表模型写一个序列化的类):class BookXlh(serializers.Serializer):

    # 单独写类
    from rest_framework import serializers
    # from rest_framework.serializers import Serializer
    from rest_framework.request import Request
    
    
    class BookXlh(serializers.Serializer):
        # 写序列化哪个字段
        id=serializers.CharField()
        title=serializers.CharField()
        price=serializers.CharField()
    
    
    
        # 用BookXlh需要导入
    以上如果不指定source,字段名字必须跟数据库名字对应起来。


    写路由
    """dj_cbv2 URL Configuration
    
    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/1.11/topics/http/urls/
    Examples:
    Function views
        1. Add an import:  from my_app import views
        2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
    Class-based views
        1. Add an import:  from other_app.views import Home
        2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
    Including another URLconf
        1. Import the include() function: from django.conf.urls import url, include
        2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
    """
    from django.conf.urls import url
    from django.contrib import admin
    
    from app01 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^books/', views.Books.as_view()),
        url(r'^book/(?P<id>d+)', views.Book.as_view()),
    ]

     source

    一旦指定了source,字段名跟source的值一定不能重复。

    # 单独写类
    from rest_framework import serializers
    # from rest_framework.serializers import Serializer
    from rest_framework.request import Request
    
    
    class BookXlh(serializers.Serializer):
        # 写序列化哪个字段
        id=serializers.CharField()
        # title=serializers.CharField()
        # 尽量让名字不要跟数据库名字重合,通过source=可以改名。
        name=serializers.CharField(source='title')
        price=serializers.CharField()
    
        # 拿publish,拿到的是名字
    
        publish_name = serializers.CharField(source='publish.name')
        publish_id = serializers.CharField(source='publish.pk')
        
        
        # 把出版社所有东西都拿出来
        publish_dic=serializers.SerializerMethodField()
        
        def get_publisn_dic(self,obj):  #obj 就是当前book对象
            return {'id':obj.publish.pk,'name':obj.publish.name}  #这个字典赋给get_publisn_dic
    
    
    
    
        # 用BookXlh需要导入
    
    
    
    # 注意:
    #1、 变量名和source='xx',不能重合
    #2、source还支持继续点
    #3、source不仅支持字段还支持执行方法
    #4、支持写方法,如下,方法一定要传一个参数,就是当前book对象
        # # 把出版社所有东西都拿出来
        # publish_dic = serializers.SerializerMethodField()
        # 
        # def get_publisn_dic(self, obj):  # obj 就是当前book对象
        #     return {'id': obj.publish.pk, 'name': obj.publish.name}  # 这个字典赋给get_publisn_dic

     序列化组件的源码 (source字段后放字段也行,放方法也可以)

    publish_dic = serializers.SerializerMethodField() 
    一定要有个方法跟他对应
    def get_publisn_dic(self, obj):
    这个方法的返回值就会赋给这个变量publish_dic
    这个方法名字也是固定的就是get+字段名

     

     获取作者

    先写类并继承

    class AuthorXlh(serializers.Serializer):
        id=serializers.CharField()
        name=serializers.CharField()
        age=serializers.CharField()

    在book类里,序列化得到。

        authors = serializers.SerializerMethodField()
        def get_authors(self,obj):
            # 所有作者requeryset对象
            authors_list=obj.authors.all()
    
            author_xlh=AuthorXlh(instance = authors_list,many=True)
            return author_xlh.data
            # 列表推导式
            # l1=['name':author.name,'id':author.pk  for author in authors_list ]

     继承ModelSerializer,这个是跟表模型绑定的序列化。

     class BookXlh(serializers.ModelSerializer):

    from app01 import models
    class BookXlh(serializers.ModelSerializer):
        # 固定写法
        class Meta:
            # 指定表模型
            model= models.Book
            # 要序列化所有字段
            fields = '__all__'
            # 只想序列化title 和 id 这两个字段
            # fields = ['title','id']
            # 如果想要publish字段显示出版社名字
            
            # 不包含什么字段
            # exclude=['title']  #不能和fields连用  不显示title字段
            
            #联表的深度 
            depth=1   #深度是1  有关联的话往下查一层   
            # 缺点 全取出来,下几层的参数不可控制
         
        # 全取出来之后,可以覆盖前面的值  ------>相当于重写某些字段 
        # 多写一个字段
        publish = serializers.CharField(source='publish.name')
        # 作者的详细信息
        authors = serializers.SerializerMethodField()
        def get_authors(self,obj):
            # 拿到
            authors_l=obj.authors.all()
            # 序列化
            author_xlh=AuthorXlh(instance=authors_l,many=True)
            
            return author_xlh.data 

    钩子函数

    from app01 import models
    class BookXlh(serializers.ModelSerializer):
        # 固定写法
        class Meta:
            # 指定表模型
            model= models.Book
            # 要序列化所有字段
            fields = '__all__'
            # 只想序列化title 和 id 这两个字段
            # fields = ['title','id']
            # 如果想要publish字段显示出版社名字
    
            # 不包含什么字段
            # exclude=['title']  #不能和fields连用  不显示title字段
    
            #联表的深度
            # depth=1   #深度是1  有关联的话往下查一层
            # 缺点 全取出来,下几层的参数不可控制
    
        title = serializers.CharField(max_length=32, min_length=2, error_messages={'max_length': '太长了'})
    
        # 局部跟全局钩子
    
        # 这个参数是title条件过来的那个参数,
    
        def validate_title(self, value):
            from rest_framework import exceptions
            if value.startswith('sb'):
                raise exceptions.ValidationError('不能以sb开头')
            return value
    
        # 全取出来之后,可以覆盖前面的值  ------>相当于重写某些字段
        # 多写一个字段
        # publish = serializers.CharField(source='publish.name')
        # # 作者的详细信息
        # authors = serializers.SerializerMethodField()
        # def get_authors(self,obj):
        #     # 拿到
        #     authors_l=obj.authors.all()
        #     # 序列化
        #     author_xlh=AuthorXlh(instance=authors_l,many=True)
        #
        #     return author_xlh.data

    增删查改的接口写法

       # 增加一本书,一般放在books里发请求上
        # 新增怎么增?-----> 提交的字典,创建一个对象去保存(快速的方法,通过序列化组件保存----->必须继承自serializers.ModelSerializer),校验通过,默认不能为空
        def post(self,request, *args, **kwargs):
            response = {'status':100,'msg':'新增成功'}
            book = request.data
            book_xlh=BookXlh(data=book)    #其实data=request.data
    
            # 什么数据都没写,默认不能为空 ,但是长度啊什么的没有限制
            # 提交的子弹通过校验
            if book_xlh.is_valid():
                book_xlh.save()
                response['book'] = book_xlh.data
            else:
                response['msg']=book_xlh.errors
    
            # return Response(book_xlh.data)
    
            return Response(response)

        # 删除,查出来直接delete
        def delete(self,request,id):
            response = {'status': 100, 'msg': '删除成功了'}
            book = models.Book.objects.filter(pk=id).first().delete()
    
            return Response(response)

    查就是获取

    # 获取一本书的
    class Book(APIView):
        def get(self, request, id):
            response = {'status': 100, 'msg': '成功了'}
            book = models.Book.objects.filter(pk=id).first()
            book_xlh = BookXlh(book, many=False)
            response['book'] = book_xlh.data
            return Response(response)
    
    
        # 修改书在book里面,put和putch请求都可以
        def put(self,request,id):
            response = {'status': 100, 'msg': '成功了'}
            book = models.Book.objects.filter(pk=id).first()
            book_xlh = BookXlh(data=request.data,instance=book)
    
            if book_xlh.is_valid():
                book_xlh.save()
                response['book'] = book_xlh.data
            else:
                response['msg']=book_xlh.errors
    
            return Response(response)

    什么是跨域?

    什么是幂等性? 



  • 相关阅读:
    前端学习笔记之BOM和DOM
    JAVA学习笔记之图解JAVA参数传递
    Python学习笔记之函数参数传递 传值还是传引用
    Java学习笔记之对象的复制和克隆
    如何科学正确的使用搜索引擎
    JAVA学习笔记之JAVA 对象引用以及赋值
    前端学习笔记之Z-index详解
    Python面试题目之Python的复制和赋值浅析
    Python面试题目之(针对dict或者set数据类型)边遍历 边修改 报错dictionary changed size during iteration
    判断对象是否为数组/函数
  • 原文地址:https://www.cnblogs.com/zhangrenguo/p/10406648.html
Copyright © 2020-2023  润新知