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
DRF将request.POST、request.FILES的数据统一封装到了data属性中,其中包含了
-
解析之后的文件、非文件数据
-
对POST、PUT、PATCH请求方式解析后的数据
-
表单类型数据、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信息,可以判断用户是否登录