01-初始drf

前后端混合开发和前后端分离

我们使用django开发的BBS,实际上就属于前后端混合开发,前端界面和后端逻辑全部都通过django来完成。

前后端分离则是后端只负责写逻辑,并且返回对应的数据,一般是json给前端,专业的前端界面让前端去完成,至于前端拿到数据之后要开发网页,还是APP活着小程序开发,都是前端自己说了算。

# 前后端混合开发
BBS项目 ---> 混合开发
django写后端,写了模板语言(Django Template Languate)
- 模板语法 --> 对模板(index.html) 渲染(后端完成) --> 模板语法的执行在后端执行
- 我们后端人员,即写了后端,也写了前端

# 前后端分离开发
- 后端只写接口 ---> 配接口  ---> 不需要动前端
- 前端只写前端 ---> 调用接口 --> APP/web 前端人员自己去处理数据的使用

前后端混合开发

img

前后端分离开发

img

API接口

概念

web后端提供给前端可以调用的接口,并且通过这个接口能拿到数据,那么这个接口称之为API接口

API接口应该包含哪些

  1. url地址
  2. 请求方式,例如 get post delete put
  3. 请求参数
    1. 地址栏中的参数,比如:127.0.0.1:8000/index?name=小满&age=3
    2. 请求体携带参数:常见的编码格式
  4. 响应结果,目前主流的响应结果为json以及xml

协议算吗?

  • 不算,API接口调用的协议都是:HTTP协议,没有别的协议

接口测试工具postman的简单使用

postman :https://www.postman.com/

image-20240410150500421

image-20240410095616801

image-20240410151140314

body的编码格式

  1. get 请求,可以在地址栏中携带数据 ---> 能不能再body体中携带数据?---> 可以

    1. 注意:可以提交到后端,但是django框架没有放在request.POST中,而是放在request.body中
  2. urlencoded编码格式 ---> 请求体中 ---> name=%E5%B0%8F%E6%BB%A1

  3. form-data格式:携带文件和数据

    1. 文件从:request.FILES.get('myfile') 获取myfile是自己写的变量名
    2. 数据从:request.POST.get() 去获取
    3. request.body 能不能用,取决于文件的大小,一般不需要打印body(会报错)
  4. json格式

    1. request.POST 是没有数据的
    2. 数据存在于request.body中 ---> 需要我们自己去转
请求方式 说明
request.POST 只有urlencode和form-data才能从request.POST取出来,json取出来是空的,只能从request.body中取出来
前端PUT请求 PUT请求的方式在request.POST中是拿不到数据的,只能通过request.body里面拿,拿到的数据是b'%..' 需要自己去再次处理
前端GET请求 一样需要通过request.body去拿数据,然后需要自己去再次处理
前端JSON请求 前端通过json请求是在request.POST中拿不到数据的,只能在request.body里面获取数据,不过可以通过json.loads(数据)反序列化,更方便处理。

restful规范

只要写api接口,就建议遵守这个规范。

REST全称是Representational State Transfer,中文意思是表述(通常译为表征性状态转义)。它首次出现在2000年Roy Fielding的博士论文中。

RESTful是一种定义web API接口的设计风格,尤其适用于前后端分离的应用模式中。

我们可以使用任何一个框架都可以实现符合restful规范的API接口

restful的十条规范

  1. 数据的安全保障

    1. url链接一般都采用https协议进行传输
    2. 注:采用https协议,可以提高数据交互过程中的安全性
  2. url地址中代接口表示

    https://api.xx.com
    https://www.xx.com/api/
    
  3. url中代版本标识

    1. 接口有版本
    https://api.xx.com/v1/login
    https://api.xx.com/v2/login
    
  4. 数据即是资源,均使用名词(可复数)

    1. 写给自己:以后所有的接口路径中,尽量不要出现动词,无法避免的除外
      1. login
      2. register
      3. ...
    https://api.xx.com/v1/users
    https://api.xx.com/v1/books
    https://api.xx.com/v1/books/3
    
  5. 请求方式决定如何操作资源【增、删、改、查 检车 curd】

    https://api.xx.com/books		- get请求:获取所有书
    https://api.xx.com/books/1		- get请求:获取主键为1的书
    https://api.xx.com/books		- post请求:新增一本书
    https://api.xx.com/books/1		- put请求:整体修改主键为1的书
    https://api.xx.com/books/1		- delete请求:删除主键为1的书
    
  6. url带搜索条件

    https://api.xx.com/goods?name=红buff  指定返回搜索的数据
    
  7. 响应中带状态码

    1xx:请求正在处理	-->	客户端看不到
    2xx::正常响应	200:请求成功 201创建成功
    3xx:重定向 301临时重定向 302永久重定向
    4xx:客户端异常 403:客户端出错 404:页面不存在
    5xx:服务器错误 500 503
    
    如果mysql-->链接不上会报错,有一个数字 ,后续遇到这个问题根据这个数字去搜索解决方案就行
    例如:MySQL报错1045
    
    自己定制状态码:1001,1002之类,配合上说明
    
  8. 响应中带信息描述(错误 或 正常)

    1. msg
    2. message
  9. 针对不同操作符合以下规范

    GET /collection: 返回资源对象的列表(数组)
    	{code: 100, msg: 成功, result: [{}, {}, {}...]}
        
    GET /collection/resource:返回单个资源对象 {}
    	{code: 100, msg: 成功, result:{}}
    	
    POST /collection:返回新生成的资源对象	{}
    
    PUT /connection/resource:返回完整的资源对象 {}
    
    DELETE /collection/resource:返回一个空文档
    	''
    	不过通常情况下,我们可以重新指定
    	{code:100, msg: 删除成功}
    
  10. 返回的数据中带链接,即url

某网站的自定义错误码参考

image-20240410163855663

序列化和反序列化

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

# 序列化: 把我们识别的数据转换成指定的格式提供给别人
python后端:把python的对象【字典,列表,对象】---》转成json/xml格式字符串过程称之为序列化

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

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

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

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

CBV源码分析

# 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)
    
  

# 8 总结:
	路由匹配成功---》会根据请求方式执行视图类中跟请求方式同名的方法

通过postman结合djangoCBV实现中间件获取IP地址和请求头加入数据库

# urls
from django.urls import path, re_path
from .views import TaskView, TaskDetailView

app_name = 'app02'

urlpatterns = [
    path('v1/', TaskView.as_view()),
    path('v1/<int:num>/', TaskDetailView.as_view()),

]

# 中间件
from django.utils.deprecation import MiddlewareMixin
from django.http import JsonResponse
from app02.models import UserLog


class IPMiddleware(MiddlewareMixin):
    def process_request(self, request):
        method = request.method
        if method.lower() == "delete":
            return JsonResponse({'code': 100, 'msg': '禁止删除!'})
        
        path = request.path
        ip = request.META.get("REMOTE_ADDR")
        user_agent = request.META.get('HTTP_USER_AGENT')
        
        UserLog.objects.create(method=method, path=path, ip=ip, user_agent=user_agent)
        
        return JsonResponse({"code": 200, "msg": f"来自{ip}的朋友,访问成功!"})
        
    
    def process_exception(self, request, exception):
        msg = '访问的页面已丢失!'
        return JsonResponse({'code': 100, "msg": msg})
   
# 模型层
from django.db import models

# Create your models here.
class UserLog(models.Model):
    ip = models.CharField(max_length=32, null=True)
    method = models.CharField(max_length=10, null=True)
    path = models.CharField(max_length=22, null=True)
    user_agent = models.CharField(max_length=255, null=True)
    time = models.DateTimeField(auto_now=True, null=True)

    class Meta:
        db_table = 'user_log'


DRF快速体验

相当于django上的app,作用是帮助我们快速实现符合restful规范的接口

安装模块 djangorestframework

注意:

  1. 需要django4以上的版本

  2. 如果安装django4以上的版本,那么MySQL版本要8以上

  3. 解决方案

    1. 更新MySQL到8以上
    2. 注释掉源码的一段代码
    # 使用mysql 作为数据库,django 4.x 会报错
    django.db.utils.NotSupportedError: MySQL 8 or later is required (found 5.7.44).
    
    def init_connection_state(self):
    	# self.check_database_version_supported() 注释掉
    
# 安装模块
pip install djangorestframework

基于drf的测试代码

# urls.py

from rest_framework.routers import SimpleRouter
from .views import TaskView

# 创建一个简单路由器对象
router = SimpleRouter()

# 注册 TaskView 视图集,并映射到 URL 路径 'task/' 上,指定名称为 'task'
router.register("task", TaskView, 'task')

urlpatterns = [
    # 这里可以添加其他 urlpatterns
]

# 将路由器的 URL 配置添加到 urlpatterns 中
urlpatterns += router.urls
# views.py

from .models import NeWTask
from .serializers import TaskSerializer
from rest_framework.viewsets import ModelViewSet

# 创建一个视图集 TaskView,继承自 ModelViewSet,用于处理 CRUD 操作
class TaskView(ModelViewSet):
    # 指定视图集的 queryset 为 NeWTask 模型的所有对象
    queryset = NeWTask.objects.all()
    # 指定视图集的 serializer_class 为 TaskSerializer
    serializer_class = TaskSerializer
# serializers.py

from .models import NeWTask
from rest_framework import serializers

# 创建一个序列化器 TaskSerializer,继承自 ModelSerializer
class TaskSerializer(serializers.ModelSerializer):
    class Meta:
        # 指定序列化器的 Meta 类中的 model 为 NeWTask 模型
        model = NeWTask
        # 指定序列化器的 Meta 类中的 fields 为 '__all__',表示序列化所有字段
        fields = "__all__"
# models.py

from django.db import models

# 创建一个模型 NeWTask,用于表示任务信息
class NeWTask(models.Model):
    # 定义任务ID字段,类型为 CharField,最大长度为 128,且唯一
    task_id = models.CharField(max_length=128, verbose_name="任务id 类型是uuid", unique=True)
    # 定义任务名称字段,类型为 CharField,最大长度为 32
    task_name = models.CharField(max_length=32, verbose_name='任务名称')
    # 定义任务时间字段,类型为 DateTimeField,用于记录任务创建时间,auto_now 参数表示每次更新时自动更新时间
    task_time = models.DateTimeField(verbose_name='任务时间', auto_now=True)
    # 定义任务描述字段,类型为 TextField,用于记录任务描述信息
    task_desc = models.TextField(verbose_name="任务描述")

    class Meta:
        # 指定模型的数据库表名为 'new_task'
        db_table = "new_task"
posted @ 2024-04-10 22:56  小满三岁啦  阅读(5)  评论(0编辑  收藏  举报