3分钟快速入门DRF

65 阅读6分钟

DRF工程

drf工程搭建

安装DRF框架

 pip install djangorestframework -i https://pypi.tuna.tsinghua.edu.cn/simple

配置 settings

 INSTALLED_APPS = [
   ...
   'rest_framework',
 ]

drf官方文档导读

这个框架提供了如下功能,让我们的代码风格更加统一,而且让你的开发工作成本更低,这个框架封装了很多很多复用的功能

  • 将请求的数据转换为模型类对象
  • 操作数据库
  • 将模型类对象转换为响应的数据如JSON格式

  • 视图封装DRF统一封装了请求的数据为request.data以及返回数据的Response方法
  • 序列化器DRF提供了序列化器可以统一便捷的进行序列化反序列化工作
  • 认证:对用户登陆进行身份验证
  • 权限:对用户权限进行认证,超级用户、普通用户、匿名用户啥的
  • 限流:对访问的用户流量进行限制减轻接口的访问压力
  • 过滤:可以对列表数据进行字段过滤,并可以通过添加django-fitlter扩展来增强支持
  • 排序:来帮助我们快速指明数据按照指定字段进行排序
  • 分页:可以对数据集进行分页处理
  • 异常处理:DRF提供了异常处理,我们可以自定义异常处理函数
  • 接口文档生成:DRF还可以自动生成接口文档

序列化器类视图,也是DRF提供的主要功能,也是我们学习的重心,也是 WEB 主要的两件事

基本视图

在drf框架中,已经封装了一款更为便捷进行接口编写的视图基类APIView,大多数情况下,都会使用这个基类进行业务视图的代码编写

Request请求

.data

DRFrequest.POSTrequest.FILES的数据统一封装到了data属性中,其中包含了

  • 解析之后的文件非文件数据

  • POSTPUTPATCH请求方式解析后的数据

  • 表单类型数据、JSON类型数据

from rest_framework.views import APIView
class ExampleView(APIView):
  def post(self, request):
      data = request.data # json/form 提交的数据
      return Response({'received data': request.data})

.query_params

DRF为了更准确的表示这是从连接里取得数据,从而把request.GET的名字更换为了request.query_params,其余操作与request.GET一样,这里只是拼写更换

from rest_framework.views import APIView
class ExampleView(APIView):
    def get(self, request):
        data = request.query_params # get 的连接传参
        return Response({'received data': request.query_params})

Response返回

目前在DRF中,我们所使用最多的就是Response这个方法,经常使用已经序列化好的数据结合Response返回

Response(data=None, status=None, template_name=None, headers=None, exception=False, content_type=None)
'''
data: 需要返回的数据
status: 状态码
headers: 头部信息
content-type: 返回数据 MIME 类型,一般不要多余设置,drf 会自动根据数据进行设置
'''

需要注意的是,在Response函数的第一个参数位置上,这个data不能是复杂结构的数据,比如ORM查询到的数据,ORM的数据需要提取出来成为Python的数据类型或者使用序列化方式将其加工才可以使用Response尽心返回

常见状态码

200 OK - [GET] # 服务器成功返回用户请求的数据
201 CREATED - [POST/PUT/PATCH] # 用户新建或修改数据成功。
202 Accepted - [*] # 表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE] # 用户删除数据成功。

序列化

什么是JSON

这一种在各个编程语言中流通的数据格式,可以在不同的编程语言中的进行数据传递交互

也就是用JSON传输数据可以让不同语言之间可以跨越语言不同的鸿沟,虽然 python 无法和 js 进行通信,但是通过JSON,就可以让两者进行数据通信,所以现在常见的接口返回的数据都是JSON格式

什么是序列化

序列化:就是把模型层的数据返回为JSON数据集

反序列化:就是把前端发来的JSON数据,类字典数据,变为模型层的数据

Json序列化

book_set = Book.objects.all()

books = []
for book in book_set:
    books.append({
        'id': book.id,
        'btitle': book.btitle,
        'price': book.price,
        'bread': book.bread,
        'bcomment': book.bcomment
    })

DRF序列化

drf中提供了类似django原生中forms组件的序列化类对象,通过对模型类或数据字段进行序列化字段映射

比如一个模型类表结构是这样的

class Book(models.Model):
    btitle = models.CharField(max_length=20, verbose_name="图书名称")
    price = models.DecimalField(max_digits=7, decimal_places=2, verbose_name="单价")
    bread = models.IntegerField(verbose_name="阅读量")
    bcomment = models.IntegerField(verbose_name="评论量")
    img = models.ImageField(upload_to='imgs/%Y/%m/%d', verbose_name="封面图片")
   
    class Meta:
        db_table = 'tb_book'
        verbose_name = '图书'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.btitle

那么对应的,可以创建出如下所示的序列化器

class BookSerializer(serializers.Serializer):
    btitle = serializers.CharField(max_length=20, label='图书名称')
    price = serializers.DecimalField(max_digits=7, decimal_places=2, label='单价')
    bread = serializers.IntegerField(default=0, required=False, label='阅读量')
    bcomment = serializers.IntegerField(default=0, required=False, label='评论量')
    img = serializers.ImageField(label='封面图片', required=False)

这样可以更为方便的将orm遍历或拿取的单独数据进行序列化返回

books = Book.objects.all()

data = BookSerializer(books, many=True).data # 这就是经过序列化器处理好的 可以返回给前端的数据

序列化示例

一个获取所有图书的示例

from django.http.response import JsonResponse
from rest_framework.views import APIView

class BooksView(APIView):
    def get(self, request):
        books = Book.objects.all()  # 查询所有图书
        serializer = BookSerializer(books, many=True)  
        # 构建序列化器对象, 需要序列化的对象不止一个,需要参数 many=True
        return JsonResponse(serializer.data, safe=False)  
        # 需要转换为Json的数据不是字典,需要设置safe=False

前后联调

以下案例主要以vue为例

前后端分离的跨域问题

安装插件

pip install django-cors-headers

修改配置信息

  • 注册corsheaders
INSTALLED_APPS = [
	...
    'corsheaders',  # 跨域
 	...
]
  • 添加中间件
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',  # 添加跨域中间件
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',  # 关闭csrf验证
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
  • 配置参数,允许所有源访问
CORS_ORIGIN_ALLOW_ALL = True

axios请求

首先在Vue项目的main.js中全局配置axios

import axios from 'axios'  // 导入axios

axios.defaults.baseURL = "http://127.0.0.1:8000/"  // 设置axios请求的默认域名,即django服务的域名
Vue.prototype.$axios = axios  // 全局挂载axios

vue获取数据进行渲染删除

<template>
  <div>
    <!--  三、循环展示所有作者  -->
    <table>
      <tr v-for="author in authorList" :key="author.id">
        <td>{{ author.id }}</td>
        <td>{{ author.name }}</td>
        <td>{{ author.gender }}</td>
        <td>{{ author.age }}</td>
        <td>
          <!--
          内层元素的事件会从内向外依次触发, 叫做冒泡机制
          想要阻止冒泡事件,需要 使用 事件修饰符 .stop
          -->
          <button @click.stop="delAuthor">删除</button>
        </td>
      </tr>
    </table>

  </div>
</template>

<script>
export default {
  name: "AuthorList",
  data() {
    return {
      authorList: []
    }
  },
  methods: {
    // 一、get请求所有作者
    getAuthorList() {
      this.$axios.get('author/')
        .then(resp => {
          console.log(resp.data)
          this.authorList = resp.data
        })
    },
    delAuthor(id) {
      this.$axios.delete('author/' + id + '/')
        .then(resp => {
          // 删除成功,重新请求数据,刷新页面
          this.getAuthorList();
        })
        .catch(err => {
          console.log('删除失败')
        })
    },  
  },
  mounted() {
    //  二、挂载,让请求自动执行
    this.getAuthorList();
  }
}
</script>

数据创建

创建数据

<template>
  <div>
    <!--   1. 使用v-model指定从表单获取数据 -->
    图书标题: <input type="text" v-model="title">
    图书价格: <input type="text" v-model="price">
    <!--  3. 使用事件绑定,将 方法和按钮进行绑定  -->
    <button @click="btn">添加</button>
  </div>
</template>

<script>
export default {
  name: "Book",
  data() {
    return {
      title: '',
      price: ''
    }
  },
  methods: {
    btn() {
      // 2. 定义方法, 使用axios发送post请求,并携带参数
      this.$axios.post('book/', {'title': this.title, 'price': this.price})
        .then(resp => {
          // 添加成功,输出响应数据
          console.log(resp.data)
        })
        .catch(err => {
          // 添加失败,输出错误信息
          console.log(err.response.data)
        })
    }
  }
}
</script>

修改数据

<template>
  <div>
    <!-- 修改之前,需要有原始数据   -->
    <!-- 一、修改图书之前,给表单设置默认值, 原本的图书数据   -->
    
    <!-- 二.修改同样也需要获取用户输入的新数据  -->
    图书标题: <input type="text" v-model="book.title">
    图书价格: <input type="text" v-model="book.price">

    <!--  四、对按钮绑定事件  -->
    <button @click="updateBook">修改</button>
  </div>
</template>

<script>
export default {
  name: "BookUpdate",
  data() {
    return {
      id: 1,
      book: {
        title: '',
        price: ''
      }
    }
  },
  methods: {
    // 1.1 获取id对应的图书数据
    getBook() {
      this.$axios.get('book/' + this.id + '/')
        .then(resp => {
          // 请求成功,将请求到的图书数据赋值给data中的变量,
          // 而表单和data中的数据进行双向绑定
          // 给data中的数据设置默认值,表单也会有默认值
          this.book.title = resp.data.title
          this.book.price = resp.data.price
        })
    },

    // 三. 定义方法,使用put提交数据
    updateBook() {
      this.$axios.put('book/' + this.id + '/', this.book)
        .then(resp => {
          console.log(resp.data)
        })
    }
  },
  mounted() {
    //  1.3 挂载
    this.getBook();
  }
}
</script>

状态保持

除了使用vuex在vue中的标准解决方案外,还有类似浏览器的内部存储机制,比如

localStorage.setItem('user', this.username) // 用户名存入localStorage,以做状态保持

let username = localStorage.getItem('user')  // 取出user信息,可以判断用户是否登录