【Django-rest-framework框架】第02回 APIView与序列化

1. Http协议

1.1 简介

HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于万维网(WWW:World Wide Web )服务器与本地浏览器之间传输超文本的传送协议。

HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。

应用层:基于tcp/ip之上,用于网络传输,广泛应用前后端的交互

1.2 四大特性

1. 基于请求响应
2. 基于TCP/IP之上用于应用层的协议
3. 无状态
4. 无/短连接

1.3 请求数据格式

1. 请求首行: 请求方式get,post, 请求地址:get携带数据,请求协议版本:0.9/1.1/2.x
2. 请求头:key:value
   Cookie:这是最重要的请求头信息之一
   Accept:浏览器可接受的MIME类型。
   Host:初始URL中的主机和端口 
   From:请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它。
   Date 当前的GMT时间。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦。
3. 请求体:post请求携带数据:三种编码格式
   1. Content-Type:application/x-www-form-urlencoded  ajax默认的数据格式,请求体中的数据以json字符串的形式发送到后台
   2. Content-Type:application/json;charset=utf-8  axios默认的数据格式,请求中的数据会以普通表单形式发送到后端
   3. Content-Type:multipart/from-data  他会将请求体的数据处理为一条消息,以标签为单元,用分隔符分开,既可以上传键值对,也可以上传文件

1.4 响应数据格式

1. 响应首行:响应协议版本,状态码,状态描述字符串
   1XX: 信息性状态码,接收的请求正在处理
   2XX:成功状态码, 请求正常处理完毕
   3XX: 重定向状态码,需要进行附加操作以完成请求
   4XX: 客户端错误状态码, 服务器无法处理请求
   5XX: 服务器错误状态码, 服务器处理请求出错
2. 响应头:key:value
   Allow: 服务器支持哪些请求方法
   Content-Length: 响应体的字节长度
   Content-Type: 响应体的MIME类型
   Content-Encoding: 设置数据使用的编码类型
   Date: 设置消息发送的日期和时间
3. 响应体:浏览器里看到的东西
   服务端返回数据 html格式,json格式

1.5 http协议版本区别

1. HTTP 0.9是最初的HTTP协议
   每个http请求都是一个tcp的链接
   只支持GET请求
   没有协议头
   无状态性
   只能传输超文本
2. HTTP 1.0
   新增了POST和HEAD命令
   可以设置contentType传输多种数据格式
   新增状态码,权限,内容编码等
3. HTTP 1.1 目前最流行
   keep-alvie,多个http请求可以使用同一个tcp
   请求和响应都支持Host头域,认为每一个服务器都绑定唯一的一个IP地址。
4. HTTP 2.0 
   增加双工模式,提高利用率
   服务端推送:不经请求向客户端发送数据
   二进制分帧层
   头信息压缩机制

2. APIView基本使用

1. 什么是rest_framework
   它是基于Django的,帮助我们快速开发符合RESTful规范的接口框架。
   安装方式有很多种,可以通过pip,或者在pycharm中安装也可以
2. APIView
   drf:是一个第三方的app,只能在djagno上使用,安装了drf后,导入一个视图类APIView,所有后期要使用drf写视图类,都是继承APIView及其子类.
   它的作用和from django.views import View中的View作用差不多,APIView是继承了View,所以执行as_view,dispatch方法都会
先走到APIView这个类中。所以通过这个APIView里的dispatch进行一层封装,对request这个对象进行改变,添加了认证、权限、频率这些

2.1 获取图书接口使用View+JsonResponsexie

views

from django.shortcuts import render
from django.http import JsonResponse
# Create your views here.
from django.views import View
from .models import Book


class BookView(View):
    def get(self, request):
        print(type(request))
        book_list = Book.objects.all()
        # book_list 是queryset对象不能直接序列化,只能通过for循环一个个列表套字典的形式
        res_list = []
        for book in book_list:
            res_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})
        return JsonResponse(res_list, safe=False, json_dumps_params={'ensure_ascii': False})  # 只能序列化字典与列表

urls

from app01.views import BookView
urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', BookView.as_view()),
]

2.2 使用APIView+drf的Response

views

# dir帮我们封装了请求类,响应类,路由类,视图类
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request


# APIView继承自django的View
class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        res_list = []
        for book in book_list:
            res_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})
        return Response(res_list)

3. APIView源码分析

# 视图类继承APIView后,执行流程就发生了变化,这个变化就是整个drf的执行流程
# 一旦继承了APIView入口,路由配置跟之前继承View是一样的-->找到图类的as_view-->APIView的as_view

@classmethod
def as_view(cls, **initkwargs):
    # 又调用了父类(View)的as_view
    view = super().as_view(**initkwargs)

    # 注释意思是从此以后,所有的请求都没有csrf的校验了
    # all other authentication is CSRF exempt.

    # 去掉csrf,在函数上加装饰器
    # @csrf_exempt
    # def index(request):
    #    pass
    # 本质等同于 index=csrf_exemot(index)
    return csrf_exempt(view)

# 请求来了,路由匹配成功会执行 View类的as_view类方法内的view闭包函数(但是没有csrf认证)
# 真正的执行,执行self.dispatch-->APIView的dispatch
def dispatch(self, request, *args, **kwargs):
    # 参数的request是原本的django原生的request
    # 下面的request,变成了drf提供的Request类的对象-->return Request(...)
    request = self.initialize_request(request, *args, **kwargs)
    # self 是视图类的对象,视图类对象.request=request 新的request
    self.request = request
        try:
            # 执行了认证,频率,权限 [不读]
            self.initial(request, *args, **kwargs)
            # 原来的View的dispatch的东西
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                 self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            response = handler(request, *args, **kwargs)
        except Exception as exc:
            # 如果出了异常,捕获异常,处理异常,正常返回
            # 在执行三大认证和视图类中方法过程中,如果出了异常,是能被捕获并处理的-->全局异常的处理
            response = self.handle_exception(exc)
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

3.1 总结

1. 只要继承APIView都没有csrf的认证了
2. 以后视图类中使用的request对象,已经变成了drf提供的Request类的对象了
3. 执行视图类的方法之前,执行了3大认证(认证,权限,频率)
4. 在执行三大认证和视图类的方法过程中只要报错,都会被捕获处理

3.2 验证APIView的使用

1. 视图类中使用的request对象,已经变成了drf提供的Request类的对象了

原生djiango 的request是这个类的对象:
<class 'django.core.handlers.wsgi.WSGIRequest'>

drf的request是这个类的对象:
<class 'rest_framework.request.Request'>
<class 'rest_framework.request.Request'>

2. request已经不是原来的request了,还能像原来的request一样使用吗?
用起来 像之前一样

print(request.method)  # get
print(request.path)  # /books/
print(request.GET)  # 原来的get请求提交的参数
print(request.POST)  # 原来post请求提交的参数

4. Request的源码分析:rest_framework.request.Request

类中有个魔法方法:__getattr__    对象.属性,属性不存在会触发它的执行
def __getattr__(self, attr): # 如果取的属性不存在会去原生django的request对象中取出来
        try:
            #反射:根据字符串获取属性或方法,self._request 是原来的request
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)
        
以后用的所有属性或方法,直接用就可以了---》(通过反射去原来的request中取的)
新的request内部有个老的request,就是 request._request
data 是个方法,被property装饰了,变成了数据属性用
   以后body体中提交的数据,都从这里取(request.POST)
   urlencoded,form-data:提交的数据在request.POST中
   json格式提交的数据,在requets.POST中没有,它在request.body中
   现在无论那种格式,都从request.data中取
query_params:get请求提交的参数,等同于request._request.GET 或  request.GET
其他:取文件也是从request.FILES中取,跟之前一样

原生requets.POST 只有urlencoded和form-data格式提交的数据,json格式提交的数据在body中,拿出来自己处理,但是drf的request中有个data,data中可以取到任意编码提交的数据 
request.data  有时候是(urlencoded,form-data)QueryDict,有时候(json)是字典

4.1 什么是魔法方法

1 在类中只要以__开头,__结尾的都称之为魔法方法
2 这种方法不需要手动调用,某种情况会自动触发
3 eg: __init__,__str__,__call__,......

5. 序列化

5.1 序列化组件介绍

是什么?drf提供的一个类,我们继承它,写自己的类
有什么用?就是用来序列化qs或单个对象的
获取所有图书接口--> qs,单个book对象转成json格式字符串,给前端--> 序列化,使用for循环,列表套字典拼接的
drf提供了一种可以快速实现序列化的类:序列化类

1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
3. 反序列化,完成数据校验功能

5.2 序列化组件基本使用

5.2.1 定义一个序列化类

serializer

# 写序列化类,给book进行序列化
# from rest_framework.serializers import Serializer
from rest_framework import serializers


class BookSerializer(serializers.Serializer):
    # 要序列化的字段,有很多字段类,字段类有很多字段属性
    # 写几个字段就序列化几个字段
    name = serializers.CharField()  # 字段类
    price = serializers.CharField()  # 字段类
    publish = serializers.CharField()  # 字段类

5.2.2 使用序列化类,序列化多条数据

views

# 写序列化类:给book进行序列化
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from .serializer import BookSerializer

class BookView(APIView):  # APIView继承自django的view
    def get(self,request):
        book_list = Book.objects.all()
        # instance表示要序列化的数据,mang=True表示序列化多条(instance是qs对象,一定要传many=True)
        ser = BookSerializer(instance=book_list, many=True)

        return Response(ser.data)

5.2.3 使用序列化类,序列化单挑数据

class BookDetailView(APIView):
    def get(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book)
        return Response(ser.data)

path('books/<int:pk>/', BookDetailView.as_view()),

5.3 反序列化

5.3.1 新增视图类

class BookView(APIView):  # APIView继承自django的View
    def post(self, request):
        # 前端传递数据,从request.data取出来
        ser = BookSerializer(data=request.data)
        if ser.is_valid():  # 表示校验前端传入的数据   没有写校验规则,现在等于没校验
            ser.save()  # 再写东西,这里会报错  调用save会触发BookSerializer的save方法,判断了,如果instance有值执行update,没有值执行create
            return Response(ser.data)
        else:
            return Response(ser.errors)

序列化类

# 写序列化类,给book进行序列化
# from rest_framework.serializers import Serializer
from rest_framework import serializers
from .models import Book


class BookSerializer(serializers.Serializer):
    # 要序列化的字段,有很多字段类,字段类有很多字段属性
    # 写几个字段就序列化几个字段
    name = serializers.CharField()  # 字段类
    price = serializers.CharField()  # 字段类
    publish = serializers.CharField()  # 字段类

    # 重写create方法
    def create(self, validated_data):
        res = Book.objects.create(**validated_data)
        return res

5.3.2 修改视图类

class BookDetailView(APIView):
    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)

序列化类

from rest_framework import serializers
from .models import Book


class BookSerializer(serializers.Serializer):
    # 要序列化的字段,有很多字段类,字段类有很多字段属性
    # 写几个字段就序列化几个字段
    name = serializers.CharField()  # 字段类
    price = serializers.CharField()  # 字段类
    publish = serializers.CharField()  # 字段类

    # 重写update方法
    def update(self, instance, validated_data):
        # instance要修改的对象
        # validated_data 校验过后的数据
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish = validated_data.get('publish')
        instance.save()
        return instance

5.3.3 删除

class BookDetailView(APIView):
    def delete(self, instance, pk):
        Book.objects.filter(pk=pk).delete()
        return Response()

6.fbv写个装饰器

装饰在视图函数,只要一装饰,以后的request就可以使用request.data,这个data无论是那种编码格式,都有数据



def outer(func_name):
    def inner(request, *args, **kwargs):
        if request.POST:
            request.data = request.POST
        else:
            request.data = json.loads(request.body)
        res = func_name(request, *args, **kwargs)
        return res

    return inner


@outer
def index(request):
    print('data', request.data)
    print(request.POST)
    return HttpResponse('ok')

7. 5接口

import json

from django.shortcuts import render

# Create your views here.
# 图书的5个接口,增删查改(crud)

from django.views import View
from .models import Book
from django.http import JsonResponse


class BookView(View):
    def get(self, request):
        book_list = Book.objects.all()
        res_list = []
        for book in book_list:
            res_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})
        return JsonResponse(res_list, safe=False)

    def post(self, request):
        name = request.POST.get('name')
        price = request.POST.get('price')
        publish = request.POST.get('publish')
        res = Book.objects.create(name=name, price=price, publish=publish)
        return JsonResponse({'name': res.name, 'price': res.price, 'publish': res.publish})


class BookDetailView(View):
    def get(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        return JsonResponse({'name': book.name, 'price': book.price, 'publish': book.publish})

    def put(self, request, pk):
        # 之前request.POST 只能取出post提交的数据,如果是put提交的取不出来
        dic = json.loads(request.body)
        name = dic.get('name')
        price = dic.get('price')
        publish = dic.get('publish')
        book = Book.objects.filter(pk=pk).first()
        book.name = name
        book.price = price
        book.publish = publish
        book.save()
        return JsonResponse({'name': book.name, 'price': book.price, 'publish': book.publish})

    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        return JsonResponse({})


from django.db import models


# Create your models here.
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)
    publish = models.CharField(max_length=32)



path('books/', views.BookView.as_view()),
path('books/<int:pk>/', views.BookDetailView.as_view()),
posted @   |相得益张|  阅读(105)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示