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)

什么是跨域?

什么是幂等性? 



原文地址:https://www.cnblogs.com/zhangrenguo/p/10406648.html