DRF学习之DRF初识

一.Web应用两种开发模式

1、前后端不分离模式

也叫前后端混合开发模式, 需要后端写模板语言(DTL), 返回的是HTML页面,比如有BBS项目,图书管理系统。

在前后端不分离的项目中,模板渲染通常是在后端完成的。这种项目结构中,后端负责处理业务逻辑、与数据库交互,并最终生成 HTML 页面,将数据填充到模板中,然后将完整的 HTML 页面发送给浏览器进行渲染。

2、前后端不分离

从程序角度来看:

  • 1个Django程序,接收请求+处理+HTML模版+用户返回
  • 2个程序:
    • 前端:vue.js/react.js/angular.js
    • 后端:Django + DjangoRestFramework

从专业度角度来看:

  • 前端,专门写前端代码 + 部署 + 版本管理 + ajax请求获取
  • 后端,API接口文档,只专注于写后端接口, 返回 json, xml格式数据

在前后端分离的项目中,模板渲染通常是在前端完成。在这种项目结构下,后端主要负责提供数据接口(通常是 JSON 格式),而前端则负责将这些数据获取并渲染到页面上。

二、什么是drf?作用是什么

1、简介

**DRF ( Django REST framework)**是一个用于构建 Web API 的强大且灵活的工具包,基于 Django Web 框架。DRF 提供了一组用于快速开发 Web API 的工具和功能,使得构建 RESTful API 变得简单和高效。也使后端开发人员更方便的编写API接口。

DRF 的设计理念是使构建 Web API 变得简单、快速且易于维护。它与 Django 框架紧密集成,为开发者提供了丰富的功能和工具,使得构建强大的 RESTful API 成为可能。

2、作用

  1. 构建 RESTful API:DRF 提供了丰富的工具和功能,使开发者能够轻松地构建符合 REST 架构风格的 Web API。通过使用 DRF,开发者可以快速地创建支持标准 HTTP 方法(如 GET、POST、PUT、DELETE 等)的 API。

  2. 序列化和反序列化:DRF 提供了序列化器(Serializers),用于处理数据的序列化和反序列化操作。这使得在 API 请求和响应中处理复杂的数据结构变得更加简单和高效。

  3. 认证和权限控制:DRF 提供了灵活的认证和权限控制系统,开发者可以轻松地实现基于 token、session、OAuth 等各种认证方式,并定义细粒度的权限控制规则。

  4. 视图类:DRF 提供了多种视图类,包括基于类的视图(Class-based Views)和函数视图(Function-based Views),以及通用视图类(Generic Views),帮助开发者快速构建 API 视图。

  5. 路由:DRF 提供了简单而强大的路由系统,可以方便地映射 URL 到视图函数或类,从而定义 API 的端点。

  6. 文档生成:DRF 集成了强大的 API 文档生成工具,可以自动生成交互式的 API 文档,帮助开发者和用户了解 API 的结构、参数和用法。

三、API接口

1、什么是API接口?

API(Application Programming Interface)接口是一组定义和描述不同软件系统之间如何互相通信的规则集合。在软件开发中,API 接口允许不同的应用程序之间共享数据和功能,从而实现更高层次的集成和互操作性。

API 接口可以是不同形式的,其中最常见的是 Web API,它通过网络(通常是通过 HTTP 协议)暴露服务,并允许其他应用程序通过发送请求和接收响应的方式与之交互。

通俗一点来说,web后端提供给前端可以调用的访问拿到数据的东西接口可以称之为API接口。API接口是前后端交互的媒介。

2、API接口应该有哪些东西?

Web API接口和一般的url链接还是有区别的,Web API接口简单概括有下面四大特点:

  • url地址:长得像返回数据的url链接
  • 请求方式:get、post、put、patch、delete
    • 采用get方式请求上方接口
  • 请求参数:Json格式或者XML格式的key-value类型数据
    • GET请求数据:地址栏中的数据----->request.GET
    • 请求体中数据:reqeust.POST'
      • 早些年 前后端交互使用XML格式,ajax其实就是异步JavaScript和XML
      • 后来 随着Json格式的出现,乃至今日都是主流
      • 未来 可能会有更高效、安全的交互格式会替代目前的Json格式
  • 响应结果:Json格式或者XML格式的数据

3、补充 Json格式与XML格式的区别

JSON(JavaScript Object Notation)和 XML(eXtensible Markup Language)都是用于在不同系统之间交换数据的常见格式,它们有以下几点区别:

  1. 语法

    • JSON:JSON 是一种轻量级的数据交换格式,采用键值对的方式表示数据。数据以键值对的形式存储,使用大括号 {} 表示对象,使用方括号 [] 表示数组。
    • XML:XML 是一种标记语言,使用标签(tag)来描述数据结构。XML 使用尖括号 <> 包围标签,标签可以嵌套表示复杂的数据结构。
  2. 可读性

    • JSON:JSON 的语法相对简洁,易于阅读和编写。对于人类来说更易于理解。
    • XML:XML 的语法相对繁琐,标签较多,使得文件相对冗长。但由于标签的结构清晰,有时也更易于理解复杂的数据结构。
  3. 数据体积

    • JSON:由于 JSON 的语法相对简洁,通常比 XML 更紧凑,数据体积更小,传输效率更高。
    • XML:XML 的冗长标签和结构使得数据文件相对较大,传输效率相对较低。
  4. 数据类型

    • JSON:JSON 支持基本数据类型(字符串、数字、布尔值、数组、对象)以及 null 值。对于简单的数据结构和前后端交互,JSON 更为常用。
    • XML:XML 可以描述更复杂的数据结构,并支持自定义数据类型和命名空间。在某些情况下,如配置文件或复杂的数据交换,XML 可能更适用。
  5. 解析和处理

    • JSON:JSON 的解析速度比 XML 更快,因为 JSON 的结构更简单。
    • XML:XML 的解析相对复杂,需要更多的处理步骤,解析速度比 JSON 慢。

总的来说,JSON 更适用于简单数据结构和前后端交互,而 XML 更适用于描述复杂数据结构和传输带有元数据的文档。在实际应用中,可以根据具体需求选择合适的格式。

四、API接口测试工具:Postman

1、安装

2、使用


**① 😗*可以切换请求方法:post,put,get等

**② params:参数:**设置一些请求的参数的地方,通常适用于一些get的没有请求体的请求

**③ authorization:**鉴权:有些请求需要验证身份,采取不同的鉴权方式,携带内容,验证你的身份(和token的作用类似)

④ headers:请求头:携带请求的一些必要信息例如:Content-Type和token等

**⑤ body:**请求体:

  • none:没有请求体

  • form-data:表单格式的内容,即可上传键值对,又可以上传文件,选择不同的内容请求头的Content-Type对应值不同 x-www-form-urlencoded:只可以上传键值对

  • raw:原始数据(有text,javascript,json,html,xml)等不同格式的数据,按接口要求上传数据 选择text,则请求头是: text/plain

  • 选择javascript,则请求头是: application/javascript

  • 选择json,则请求头是: application/json (如果想以json格式传参,就用raw+json就行了)

  • 选择html,则请求头是: text/html

  • 选择application/xml,则请求头是: application/xml

  • binary:相当于Content-Type:application/octet-stream,只可以上传二进制数据,通常用来上传文件,由于没有键值,所以,一次只能上传一个文件

  • GraphQL:数据类型对参数具体的类型会有一定的标准

  • Cookies:验证身份的一种方式

⑥ Pre-request Script:预请求脚本也叫做前置脚本,在请求发生之前先发生的脚本,请求中需要用的一些内容,可太勇敢前置脚本获得

⑦ tests:后置脚本,请求之后发生的一些脚本,此接口的响应有一些其他接口用的内容,可通过tests传递出去

可以设置获取变量也可以设置断言方法

⑧ settings:设置:设置这个请求需要的一些条件,比如证书,请求头设置,等,根据需要设置

  • body:响应体

    • Pretty:响应展示的视图格式化显示:JSON,XML,HTML,Text,Auto这几种类型的响应

    • Raw:响应的原始文本,未格式化的

    • Preview:视图:一些html的响应方便查看

    • Visualize:可视化:一些图片验证码的响应就可以在此处查看

  • cookies:当响应中有cookies时会在这里显示

  • headers:响应头:展示响应头的信息

  • test results:测试结果:当在请求中有断言的时候,测试的结果会在此处展示

3、补充 body的编码格式

  • get 请求,可以在地址栏中带数据---》能不能在body体中带数据---》能!
    • -注意:可以提交到后端,但是django框架没有放在request.POST中,放在request.body中
  • urlencoded 编码格式---》请求体中--》name=%E5%BD%AD%E4%BA%8E%E6%99%8F&age=38
  • form-data格式:携带文件和数据
    • 文件:request.FILES.get('myfile')
    • 数据:request.POST.get()
    • request.body 能不能用,取决于文件大小,一般不用打印body(会报错)
      • 因为http是基于tcp的协议,而tcp是流式协议,一次性发达文件会报错
  • json格式
    • request.POST 是没有数据的
    • request.body 中有 ---> 需要自己在后端转成自己需要的格式数据

4、postman创建文件,导入和导出

  • 这些操作需要我们先登录才能操作,所以记得注册一个账号,反正免费
  • 创建完请求之后一定要记得保存

五、RESTful API规范

RESTful API 是一种基于 REST 架构风格设计的 API,它遵循一组约定和规范,以提供统一的接口,使得不同系统之间能够更轻松地进行通信和交互。以下是设计 RESTful API 时应遵循的一些规范:

1、数据的安全保障

  • url链接一般都采用https协议进行传输
  • 注:采用https协议,可以提高数据交互过程中的安全性

2、url地址中带接口标识

3、url中带版本标识

4、数据即是资源,均使用名词(可复数)

5、请求方式决定如何操作资源【增,删,改,查:curd】

6、url中带搜索条件

7、响应中带状态码

  • http响应状态码
    • 1xx :请求正在处理 --> 客户端看不到
    • 2xx :正常响应---> 经常看到的有
      • 200:请求成功
      • 201:创建成功
    • 3xx :重定向---》偶尔看到 301 和 302
    • 4xx :客户端异常---》403: 404
    • 5xx: 服务端错误 500
  • 有趣的状态码记忆方法:
  • 自定制状态码
    • 例如:mysql--> 连接连不上--> 有个错误提示信息:错误码,错误描述
    • 我们也可以自己定制,但是一定要把错误提示码对应的错误消息公布出来让别人知道

8、响应中带信息描述(错误,正常)

  • 例如:code,message
  • { code: 1000, message: 成功}

9、针对不同操作符合以下规范

  • 统一规范的错误响应格式,包括错误码、错误信息和可能的解决方案。客户端可以根据错误信息快速定位问题所在。

  • 比如:

  • GET /collection:返回资源对象的列表(数组)

    • {code:100,msg:成功,results:[{},{},{}]}
  • GET /collection/resource:返回单个资源对象 {}

    • {code:100,msg:成功,result:{}}
  • POST /collection:返回新生成的资源对象 {}

  • PUT /collection/resource:返回完整的资源对象 {}

  • DELETE /collection/resource:返回一个空文档

    • {code:100,msg:删除成功}

10、返回数据中带链接

RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。

比如,当用户向api.example.com的根目录发出请求,会得到这样一个文档。

python 复制代码
{"link": {
  "rel":   "collection https://www.example.com/zoos",
  "href":  "https://api.example.com/zoos",
  "title": "List of zoos",
  "type":  "application/vnd.yourformat+json"
}}

上面代码表示,文档中有一个link属性,用户读取这个属性就知道下一步该调用什么API了。rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),href表示API的路径,title表示API的标题,type表示返回类型。

Hypermedia API的设计被称为HATEOAS。Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。

六、序列化与反序列化

api接口开发,最核心最常见的一个过程就是序列化,所谓序列化就是把数据转换格式,序列化可以分两个阶段:【序列化值的是转换数据格式:序列化,反序列化】

1、序列化

序列化: 把我们识别的数据转换成指定的格式提供给别人

站在python后端来看:把python的对象【字典,列表,对象】---> 转成json/xml格式字符串过程称之为序列化。

从数据库取出来---> 给前端 也可以叫序列化

例如:我们在django中获取到的数据默认是模型对象(qs/单个对象),但是模型对象数据无法直接提供给前端或别的平台使用,所以我们需要把数据进行序列化,变成字符串或者json数据,提供给别人。

2、反序列化

反序列化:把别人提供的数据转换/还原成我们需要的格式。

前端给我们的数据----> 我们保存到数据库中 也可以叫反序列化

例如:前端js提供过来的json/xml数据,对于python而言就是字符串,我们需要进行反序列化换成模型类对象,这样我们才能把数据保存到数据库中

3、js中的序列化和反序列化

js 如何把对象序列化成字符串:【JSON.stringify()】,把字符串反序列化成对象:【JSON.parse()】

4、小结

  • 序列化:
    • drf称为 read(读取数据)
    • queryset --- > json
    • 返给前端
  • 反序列化:
    • drf称为 write(写入数据)
    • 字符串 --- > json
    • 接收前端的数据

七、初步编写API

1、基于drf实现

(1)安装

python 复制代码
pip install djangorestframework

(2)注册

python 复制代码
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
    'rest_framework'
]

(3)返回数据界面

返回的是数据 + 嵌套好看的页面

路由

python 复制代码
path('login/',views.login)
path('info/',views.InfoView.as_view())

视图

  • FBV
python 复制代码
from rest_framework.response import Response
from rest_framework.decorators import api_view


@api_view(["GET"])
def login(request):
    return Response({'status':True,'message':"success"})
  • CBV
python 复制代码
from rest_framework.response import Response
from rest_framework.views import APIView
class InfoView(APIView):
    def get(self,request):
        Response({'status': True, 'message': "success"})

八、DRF快速使用

1、安装DRF

  • 使用pip包管理器,在终端中运行以下命令来安装DRF

    pip install djangorestframework

  • 最新的drf,必须django 3.x以上,配置MySQL3.8版本以上

2、注册

python 复制代码
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
    'rest_framework'
]

3、创建表

python 复制代码
import uuid
from django.db import models

class Task(models.Model):
    task_id = models.UUIDField(unique=True, default=uuid.uuid4, editable=False)
    task_name = models.CharField(max_length=255, db_index=True)
    task_time = models.DateTimeField(auto_now_add=True)
    task_desc = models.TextField()

4、创建序列化器(Serializer):

  • 序列化器是DRF中一个重要的概念,它将Python对象转换为JSON等可被传输的格式,并可以反序列化接收到的数据。

  • 在你的应用程序中创建一个名为serializers.py的文件,并定义你的序列化器类。

  • 一个示例:

    from rest_framework import serializers
    from .models import Task
    class TaskSerializer(serializers.ModelSerializer):
    class Meta:
    model = Task
    fields = 'all'

  • 在这个示例中,我们使用ModelSerializer来自动创建序列化器类。

5、创建视图(View)

  • 在你的应用程序中创建一个名为views.py的文件,并定义视图类。

  • 一个示例:

    from .models import Task
    from .serializer import TaskSerializer
    from rest_framework.viewsets import ModelViewSet
    class TaskView(ModelViewSet):
    queryset = Task.objects.all()
    serializer_class = TaskSerializer

6、配置URL路由

  • 在你的应用程序的urls.py文件中,定义DRF的URL路由。

  • 一个示例:

    from rest_framework.routers import SimpleRouter
    router = SimpleRouter()
    router.register('task', TaskView, 'task')
    urlpatterns = [
    ]
    urlpatterns += router.urls

  • 通过上述配置,当访问127.0.0.1:8000/task/时,将会调用TaskView视图。

7、测试代码

python 复制代码
import requests

# res = requests.get('http://127.0.0.1:8000/app01/task/')
# print(res.text)
from threading import Thread


# 多线程新增数据
def task(task_id, task_name):
    data = {
        "task_id": task_id,
        "task_name": task_name,
        "task_time": "2024-04-10 11:54:56",
        "task_desc": "很重要,不能忘记"
    }
    res = requests.post('http://127.0.0.1:8000/app01/task/', json=data)
    print(res.text)


if __name__ == '__main__':
    l = []
    for i in range(30):
        t = Thread(target=task, args=['asdfads-33-as-%s' % i, '任务00%s' % i])
        t.start()
        l.append(t)
    for i in l:
        i.join()

九、CBV源码剖析

(1)cbv写法:

  • 视图中写视图类,继承View,写跟请求方式同名的方法
python 复制代码
class TaskView(View):
    def get(self,request):
        return 四件套
  • 在路径用写
python 复制代码
path('task/', TaskView.as_view())
  • 如上写法,为什么能够执行

(2)前置条件

  • 前端请求,一旦路径匹配成功,就会执行
    • TaskView.as_view()(request传入,)
  • 入口在
    • TaskView.as_view()--->执行结果---》View中有个as_view类的绑定方法
python 复制代码
@classmethod
def as_view(cls, **initkwargs):
    def view(request, *args, **kwargs):
        self = cls(**initkwargs)
        res=self.dispatch(request, *args, **kwargs)
        return res
    return view
  • 执行结果是view 的内存地址: 请求来了,执行view(request)
python 复制代码
path('books/', view)
  • 执行 View类中的as_view方法中的内层的view函数,路由匹配成功,本质是在执行
python 复制代码
self.dispatch(request, *args, **kwargs)
  • self是谁的对象?
    • BookView的对象
  • 去BookView中dispatch,找不到
    • 去父类,View中找到了
  • View这个类的dispatch
python 复制代码
def dispatch(self, request, *args, **kwargs):
    # 取出请求方式,转成小写,判断在不在列表中  get请求在
    if request.method.lower() in self.http_method_names:
        # handler=getattr(BookView的对象,'get')   
        # handler就是BookView类中的get方法
        handler = getattr(self, request.method.lower())
    else:
        handler = self.http_method_not_allowed
        # 执行 BookView类中的get方法 (request)
        return handler(request, *args, **kwargs)
  • 最终本质跟写fbv的执行流程一样

(3)最终结论

  • 什么请求方式,就会执行视图类中的什么方法
python 复制代码
# 1 在路由中:path('index/',IndexView.as_view())

# 2 请求来了---》路由匹配成功--》执行 IndexView.as_view()(request)
	-看View类的as_view的返回结果[可以加括号执行]---》猜:函数内存地址
    
# 3 View类的as_view
    def as_view(cls, **initkwargs):
        def view(request, *args, **kwargs):
            self = cls(**initkwargs) # cls--》IndexView
            return self.dispatch(request, *args, **kwargs)
        return view
# 4 IndexView.as_view() 本质就是 --》view--》内存函数view

# 5 IndexView.as_view()(request)---》view(request)
# 6 本质 执行 self.dispatch--》self 是对象--》谁的对象?视图类的对象IndexView类的对象
	return self.dispatch(request, *args, **kwargs)

# 7 IndexView没有dispatch--》View类中的dispatch
	# request 当次请求的requets
    def dispatch(self, request, *args, **kwargs):
        #1 取出请求方式,转成小写,判断在不在列表中  get请求在
        #2 self.http_method_names 
        if request.method.lower() in self.http_method_names:
            # 3 反射:去 self IndexView类的对象中通过字符串get找属性或方法
            # 找到了get方法,赋值给了handler
            handler = getattr(self, request.method.lower())
        else:
            handler = self.http_method_not_allowed
            # 4 执行handler--》本质是get(request)
            # 执行了IndexView类中的get方法--》把request传进去了
        return handler(request, *args, **kwargs)

十、基于APIView的5个接口使用

1、视图类

python 复制代码
from rest_framework.response import Response
from rest_framework.views import APIView  # # APIView继承了djagno原来的View
from app01.serializer import TaskSerializer
from app01.models import Task


class TaskView(APIView):
    def get(self, request):
        tasks = Task.objects.all()
        # drf提供了序列化类
        serialized_tasks = TaskSerializer(instance=tasks, many=True)  # 序列化
        return Response({'code': 100, 'msg': '查询所有成功', 'results': serialized_tasks.data})

    def post(self, request):
        serialized_task = TaskSerializer(data=request.data)  # 反序列化
        if serialized_task.is_valid():  # 数据校验---》有些不合法的禁止
            serialized_task.save()  # 保存到数据库中
        return Response({'code': 100, 'msg': '创建成功'})


class TaskDetailView(APIView):
    def get(self, request, *args, **kwargs):
        try:
            task = Task.objects.filter(id=kwargs['id']).first()
            serialized_task = TaskSerializer(instance=task, many=False)  # 序列化
            return Response({'code': 100, 'msg': '查询单个成功', 'result': serialized_task.data})
        except Exception:
            return Response({'code': 200, 'msg': '任务不存在'})

    def put(self, request, *args, **kwargs):
        try:
            task = Task.objects.filter(id=kwargs['id']).first()
            serialized_task = TaskSerializer(instance=task, data=request.data)  # 反序列化
            if serialized_task.is_valid():  # 数据校验---》有些不合法的禁止
                task.save()  # 重保存到数据库中
            return Response({'code': 100, 'msg': '更新成功'})
        except Exception:
            return Response({'code': 200, 'msg': '任务不存在'})

    def delete(self, request, *args, **kwargs):
        try:
            Task.objects.filter(id=kwargs['id']).delete()
            return Response({'code': 100, 'msg': '删除成功'})
        except Task.DoesNotExist:
            return Response({'code': 200, 'msg': '任务不存在'})

2、序列化类

python 复制代码
from rest_framework import serializers
from .models import Task


class TaskSerializer(serializers.ModelSerializer):
    class Meta:
        model = Task
        fields = '__all__'

3、模型类

python 复制代码
import uuid
from django.db import models

class UserLog(models.Model):
    ip = models.CharField(max_length=255)
    visit_time = models.DateTimeField(auto_now=True)
    method = models.CharField(max_length=32)
    path = models.CharField(max_length=64)
    user_agent = models.CharField(max_length=255)


class Task(models.Model):
    task_id = models.UUIDField(unique=True, default=uuid.uuid4, editable=False)
    task_name = models.CharField(max_length=255, db_index=True)
    task_time = models.DateTimeField(auto_now_add=True)
    task_desc = models.TextField()

4、路由

python 复制代码
from django.urls import path
from app01 import views

urlpatterns = [
    path('task/', views.TaskView.as_view()),
    path('task/<int:id>', views.TaskDetailView.as_view()),
]
相关推荐
龙哥说跨境9 分钟前
如何利用指纹浏览器爬虫绕过Cloudflare的防护?
服务器·网络·python·网络爬虫
小白学大数据25 分钟前
正则表达式在Kotlin中的应用:提取图片链接
开发语言·python·selenium·正则表达式·kotlin
flashman91126 分钟前
python在word中插入图片
python·microsoft·自动化·word
菜鸟的人工智能之路29 分钟前
桑基图在医学数据分析中的更复杂应用示例
python·数据分析·健康医疗
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
秃头佛爷3 小时前
Python学习大纲总结及注意事项
开发语言·python·学习
深度学习lover4 小时前
<项目代码>YOLOv8 苹果腐烂识别<目标检测>
人工智能·python·yolo·目标检测·计算机视觉·苹果腐烂识别
API快乐传递者5 小时前
淘宝反爬虫机制的主要手段有哪些?
爬虫·python
阡之尘埃7 小时前
Python数据分析案例61——信贷风控评分卡模型(A卡)(scorecardpy 全面解析)
人工智能·python·机器学习·数据分析·智能风控·信贷风控
丕羽10 小时前
【Pytorch】基本语法
人工智能·pytorch·python