当前位置:   article > 正文

Django丨REST framework_django rest framework

django rest framework

一、引入Django REST framework

1.Web应用模式

在开发web应用中,有两种应用模式:前后端不分离和前后端分离。

  • 前后端不分离

    • 前端页面看到的效果都是由后端控制,由后端渲染页面或重定向。
    • 前端与后端的耦合度很高。
    • 比较适合纯网页应用,当后端对接App时,返回的网页接口不适用于前端App应用
  • 前后端分离

    • 由前端服务器返回静态文件,后端仅返回前端所需要的的数据,不再渲染HTML页面
    • 前端与后端的耦合度相对较低
    • 后端仅需要开发一套逻辑对外提供数据 ,可适用于网页和App的前端
    • 在前后端分离的应用模式中,将后端开发的每个视图都称为一个借口,或者API,前端通过访问接口来对数据进行增删改查。

前后端分离开发示例
获取图书数据 Web API

  • 请求方式:get
  • 请求路径:book/1
  • 请求参数:图书id 1
  • 请求结果:json {‘books’:[{‘name’:‘西游记’,‘bread’:20},{‘name’:‘三国’,‘bread’:30}]}

前端

<ul>
	<li v-for="book in data">
		{{book.name}}
		{{book.bread}}
	</li>
</ul>

axios.get('http:\\127.0.0.1:8000/book/1')
	# 200系列状态码,执行then
	.then(response => {
		this.data = response.data.books
	})	
	# 返回400/500状态码,执行catch
	.catch(response = >{
		response.data.error
	})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

后端

url(r'book/(?P<pk>\d+)',views.BookView.as_view())

class BookView(View):
	def get(self,request,pk):
		# 1.获取数据
		# 2.查询数据
		try:
			book = Book.object.get(id=pk)
		except:
			return HttpResponse()
		data = {"name":book.name, "bread":book.bread}
		return JsonResponse(data)
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.RESTful

在前后端分离的应用模式里,API接口普遍采用RESTful设计风格

  • 1.域名:应该尽量将API部署在专有域名之下
    https://api.example.com
    如京东:https://shouji.jd.com/
    如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下
    https:example.org/api/

  • 2.版本:应该将API的版本号放入url
    http://www.example.com/app/1.0/foo
    http://www.example.com/app/1.1/foo
    http://www.example.com/app/2.0/foo
    另一种做法是将版本号放在HTTP头信息中,因为不同的版本可以理解成同一种资源的不同表现形式,所以应该采用同一种url,版本号可以放在HTTP请求头信息的Accept字段中进行区分。
    Accept: vnd.example-com.foo+json; version=1.0
    Accept: vnd.example-com.foo+json; version=1.1
    Accept: vnd.example-com.foo+json; version=2.0

  • 3.路径
    路径又称“终点”,表示API的具体网址,每个网址代表一种资源。

    • (1) 资源作为网址,只能有名词,不能有动词,所用名词往往与数据库的表名对应;此外,利用HTTP方法可以分离网址中的资源名称的操作。
      如:
      GET /products :将返回所有产品清单
      POST /products :将产品新建到集合
      GET /products/4 :将获取产品 4
      PATCH(或)PUT /products/4 :将更新产品 4
    • (2) API中的名词 应该使用复数,无论子资源还是所有资源
      如:
      获取单个产品:http://127.0.0.1:8080/AppName/rest/products/1
      获取所有产品: http://127.0.0.1:8080/AppName/rest/products
  • 4.HTTP动词
    对于资源的具体操作类型,由HTTP动词表示,常用的HTTP动词有下面四个。
    GET:从服务器取出资源(一项或多项)。
    POST:在服务器新建一个资源。
    PUT:在服务器更新资源(客户端提供改变后的完整资源)。
    DELETE:从服务器删除资源。
    如:
    GET /zoos:列出所有动物园
    POST /zoos:新建一个动物园(上传文件)
    GET /zoos/ID:获取某个指定动物园的信息
    PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
    PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
    DELETE /zoos/ID:删除某个动物园
    GET /zoos/ID/animals:列出某个指定动物园的所有动物
    DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

  • 5.过滤信息以查询字符串进行
    如果记录数量很多,API应该提供参数,过滤返回结果。
    下面是一些常见的参数:

    ?limit=10:指定返回记录的数量
    ?offset=10:指定返回记录的开始位置。
    ?page=2&per_page=100:指定第几页,以及每页的记录数。
    ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
    ?animal_type_id=1:指定

  • 6.状态码
    后端执行成功或失败以状态码的形式告诉前端,处理成功返回200系列状态码,执行前端then里面的代码;处理失败返回400/500系列状态码,执行catch里面的代码。

  • 200 OK :服务器成功返回用户请求的数据
  • 201 CREATED :用户新建或修改数据成功。
  • 202 Accepted :表示一个请求已经进入后台排队(异步任务)
  • 204 NO CONTENT :用户删除数据成功。
  • 400 INVALID REQUEST :用户发出的请求有错误,服务器没有进行新建或修改数据的操作
  • 401 Unauthorized :表示用户没有权限(令牌、用户名、密码错误)。
  • 403 Forbidden :表示用户得到授权(与401错误相对),但是访问是被禁止的。
  • 404 NOT FOUND :用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
  • 406 Not Acceptable :用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
  • 410 Gone:用户请求的资源被永久删除,且不会再得到的。
  • 422 Unprocesable entity : 当创建一个对象时,发生一个验证错误。
  • 500 INTERNAL SERVER ERROR :服务器发生错误,用户将无法判断发出的请求是否成功
  • 7.错误处理
    如果状态码是4xx,服务器就应该向用户返回出错信息,一般来说,返回的信息中将error作为键名,出错信息作为键值。
{
	error:"出错信息"
}
  • 1
  • 2
  • 3
  • 8.返回结果
    针对不同操作,服务器向用户返回的结果应该符合以下规范:
    • GET /collection:返回资源对象的列表(数组)
    • GET /collection/resource:返回单个资源对象
    • POST /collection:返回新生成的资源对象
    • PUT /collection/resource:返回完整的资源对象
    • PATCH /collection/resource:返回完整的资源对象
    • DELETE /collection/resource:返回一个空文档
  • 9.其他
    服务器返回的数据格式,应该尽量使用JSON,避免使用XML

3.使用Django开发REST接口示例

使用图书英雄案例来写一套支持图书数据增删改查的REST API接口
① 接口设计

  • 增加图书
请求方式请求路径请求参数返回结果
POSTbooks/title、bpub_date (请求体json)保存后的图书数据books=[{btitle:“西游记”,bpub_date:2017},{…}]
  • 删除图书
请求方式请求路径请求参数返回结果
DELETEbooks/1id (路径)
  • 修改图书
请求方式请求路径请求参数返回结果
PUTbooks/1修改字段内容(json)、id (路径)更新后的图书数据 book={ btitle:“xxx”,bpub_date:“xxx”}
  • 查询图书

查询单一图书

请求方式请求路径请求参数返回结果
GETbooks/1id (路径)book={ btitle:“xxx”,bpub_date:“xxx”}

查询所有图书

请求方式请求路径请求参数返回结果
GETbooks/1books=[{btitle:“西游记”,bpub_date:2017},{…}]

② 后端代码实现

urlpatterns = [
	url(r'^books/$', views.BooksView.as_view()),
	url(r'^books/(?P<pk>\d+)/', views.BookView.as_view()),
]




from django.views import View
from django.http import JsonResponse
from book.models import BookInfo
import json


class BooksView(View):

	def get(self,request):
		"""获取所有图书""""
		books = BookInfo.objects.all()
		data = []
		for book in books:
			data.append({
				"id": book.id,
				"btitle": book.btitle,
				"bpub_date": book.bpub_date
			})
		# safe=False,是将列表[]转成json中的数组"[]"
		return JsonResponse(data, safe=False)
		
	def post(self,request):
		"""保存图书"""
		# 1、获取前端数据
		data = request.body.decode()
		data_dict = json.loads(data)
		btitle = data_dict.get("btitle")
		bpub_date = data_dict.get("bpub_date")
		# 2、验证数据
		if btitle == "python":
			return JsonResponse({"error": "错误的书名"},status=400)
		# 3、保存数据
		book = BookInfo.objects.create(btitle=btitle, bpub_date=bpub_date)
		# 4、返回结果
		return JsonResponse({
			"id": book.id,
			"bpub_date": book.bpub_date,
			"btitle": book.btitle
		})

class BookView(View):
	def get(self,request,pk):
		"""查询一本图书"""
		try:
			book = BookInfo.objects.get(id=pk)
		except:
			return JsonResponse({
				"id": book.id,
				"btitle": book.btitle,
				"bpub_date": book.bpub_date
			})
	def put(self,request,pk):
		"""修改一本图书"""
		# 1、获取数据
		data = request.body.decode()
		data_dict = json.loads(data)
		btitle = data_dict.get("btitle")
		bpub_date = data_dict.get("bpub_date")
		# 2、校验数据
		# 3、更新数据
		# 这种方法得到的并不是对象,而是得到更新数据的个数
		# book = BookInfo.objects.filter(id=pk).update(btitle=btitle,bpub_date=bpub_date)
		try:
			book = BookInfo.objects.get(id=pk)
		except:
			return JsonResponse({"error": "错误的id"},status=400)
		book.btitle = btitle
		book.bpub_date = bpub_date
		book.save()
		# 4、返回结果
		return JsonResponse({
			"id": book.id,
			"btitle": book.btitle,
			"bpub_date": book.bpub_date
		})
	def delete(self,request,pk):
		"""删除一本图书"""
		try:
			book = BookInfo.objects.get(id=pk)
		except:
			return JsonResponse({"error": "错误的id"}, status=400)
		
		book.delete()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91

4.序列化与反序列化

  • 序列化:将程序的一个数据结构类型转换成其他格式(字典、JSON、XML等),如,获取数据库中的数据对象所对应的字段数据转化为json的过程(从后端服务到前端服务)
  • 反序列化:将其他格式(字典、JSON、XML等)转换为程序中的数据,如,获取前端数据,验证数据到保存或更新数据获取新的数据对象的过程(从前端服务到后端服务)

5.Django REST framework简介

  • Django REST framework 框架是一个用于构建Web API的强大而又灵活的工具,通常简称为DRF框架或REST framework。
  • 特点:
    • 提供了定义序列化器Serializer的方法,可以快速根据Django ORM或者其他库自动序列化/反序列化。
    • 提供了各种丰富的类视图、Mixin扩展类,简化视图的编写
    • 丰富的定制层级:函数视图、类视图、视图集合到自动生成API,满足各种需要
    • 多种身份认证和权限认证方式的支持
    • 内置了限流系统
    • 直观的API web界面
    • 可扩展性,插件丰富
  • 官方文档

二、DRF工程搭建

环境安装与配置

  • 1.安装DRF
pip install djangorestframework
  • 1
  • 2.添加rest_framework应用
    在settings.py的INSTALLED_APPS中添加’rest_framework’
INSTALLED_APPS = [
    ...
    'rest_framework',
]

  • 1
  • 2
  • 3
  • 4
  • 5

三、Serializer序列化器

序列化器的作用:

  • 进行数据的校验
  • 对数据对象进行转换

1.定义Serializer

在应用的目录下创建serializers.py文件,Serializer使用类来定义,须继承自rest_framework.serializers.Serializer.

from rest_framework import serializers


class BookSerializer(serializers.Serializers):
	# 根据模型类字段进行定义字段
	btitle=serializers.CharField()
	bpub_date=serializers.DateField()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

注:serializer不是只能为数据库模型类定义,也可以为非数据库模型类的数据定义,serializer是独立于数据库之外的存在。

2.序列化使用

  • 1 基本使用
from book.serializers import BookSerializer

class BookView(View):
	def get(self,request,pk):
		"""查询一本图书"""
		try:
			# 查询出一个图书对象
			book = BookInfo.objects.get(id=pk)
		except:
			# return JsonResponse({"id": book.id,"btitle": book.btitle,"bpub_date": book.bpub_date})
			# 1.构造序列化器对象
			serializer = BookSerializer(book)
			# 2.通过data方法获取序列化数据 
			data = serializer.data
			return JsonResponse(data)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 2 返回多条数据,通过添加many=True参数
from book.serializers import BookSerializer


class BooksView(View):

	def get(self,request):
		"""获取所有图书""""
		books = BookInfo.objects.all()
		# data = []
		# for book in books:
			# data.append({"id": book.id,"btitle": book.btitle,"bpub_date": book.bpub_date})
		serializer = BookSerializer(books,many=True)
		return JsonResponse(serializer.data, safe=False)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 3 关联对象嵌套序列化
    如果需要序列化的数据包含有其他关联对象,则对关联对象数据的序列化需要指明。
    对于关联字段可以采用以下几种方式:
    • PrimaryKeyRelatedField
      此字段将被序列化为关联对象的主键,返回关联对象的id值
      heroinfo_set = serializers.PrimaryKeyRelatedField(read_only=True,many=True)
      注:read_only=True,该字段只能用于序列化操作,不能用于反序列化;write_only=True,只能用于反序列化操作,不能用于序列化操作;many=True,关联的对象数据可能包含多条数据。
      返回结果示例:
{"btitle":"天龙八部", "bpub_date": "1986-07-24", "heroinfo_set":[6,7,8,9]}
  • 1
  • StringRelatedField
    此字段将被序列化为关联对象的字符串表示方式(即__str__方法的返回值),返回模型类中__str__方法中返回的数据。
    heroinfo_set = serializers.StringRelatedField(read_only=True,many=True)
    返回结果示例:
{"btitle":"天龙八部", "bpub_date": "1986-07-24", "heroinfo_set":["乔峰", "段誉","虚竹","王语嫣"]}
  • 1
  • 使用关联对象的序列化器
    返回指定的序列化器中指定的字段,使用时要避免序列胡器之间相互调用。
    heroinfo_set = HeroSerializer(many=True)
    返回结果示例:
   {
    	"btitle":"天龙八部", 
     	"bpub_date": "1986-07-24", 
     	"heroinfo_set":[
     		{"hname":"乔峰","hgender":1},
     		{"hname": "段誉","hgender":1},
     		{"hname":"虚竹","hgender":1},
     		{"hname":"王语嫣","hgender":0)
     	]
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

例如:定义图书数据的序列化器时,关联对象英雄的序列化

from rest_framework import serializers

class HeroSerializer(serializers.Serializer):
	hname = serializers.CharField()
	hgender = serializers.IntegerField()


class BookSerializer(serializers.Serializers):
	# 根据模型类字段进行定义字段
	btitle=serializers.CharField()
	bpub_date=serializers.DateField()
	# heroinfo_set = serializers.PrimaryKeyRelatedField(read_only=True,many=True)
	# heroinfo_set = serializers.StringRelatedField(read_only=True,many=True)
	heroinfo_set = HeroSerializer(many=True)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

3.反序列化使用

  • 1.字段参数验证的使用
    选项参数
参数名称作用
max_length最大长度
min_length最小长度
allow_blank是否允许为空
max_value最大值
min_value最小值

通用参数

参数名称说明
read_only表明该字段仅用于序列化输出,默认False
write_only表明该字段仅用于反序列化输入,默认False
default反序列化时使用的默认值
calss BookSerializer(serializers.Serializaer):
	btitle = serializers.CharField(max_length=11, min_length=5)
	bpub_date = serializers.DateField(write_onlt=True)
	bread = serializers.IntegerField(read_only=True)
	bcomment = serializers.IntegerField(default=2, min_value=1, max_value=60)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 2.验证
    • 使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。
    • 在获取反序列化的数据前,使用is_valid()方法进行验证,验证成功返回True,否则返回False。
    • 验证失败,可以通过序列化器对象的errors属性获取错误信息,返回字典,包含了字段和字段错误;如果是非字段错误,可以通过修改REST framework配置中的NON_FIELD_ERRORS_KEY来控制错误字典中的键名。
    • 验证成功后,可以通过序列化器对象的validated_data属性获取数据。

通过构造序列化器对象,并将要反序列化的数据传递给data构造参数,进而进行验证

from django.views import View
from django.http import JsonResponse
from book.models import BookInfo
import json


class BooksView(View):
		
	def post(self,request):
		"""保存图书"""
		# 1、获取前端数据
		data = request.body.decode()
		data_dict = json.loads(data)
		# btitle = data_dict.get("btitle")
		# bpub_date = data_dict.get("bpub_date")
		
		# 2、验证数据
		ser = BookSerializer(data=data_dict)
		ser.is_valid(raise_exception=True)
		print(ser.errors)
		# 字典 {'btitle': [ErrorDetail(string='This field is required.', code='required')]}
		
		# 3、保存数据
		# 4、返回结果
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

说明:
raise_exception=True参数开启,is_valid()方法会在验证失败时抛出异常,向前端返回400 Bad Request响应。
自定义验证:
在serializers.py文件对应的序列化器中定义验证方法,is_valid()对字段验证没有问题后,会调用自定义的验证方法

  • ① validate_<field_name>:单一字段验证
    对<field_name>字段进行验证,value即对于的字段值
from rest_framework import serializers


calss BookSerializer(serializers.Serializaer):
	btitle = serializers.CharField(max_length=11, min_length=5)
	bpub_date = serializers.DateField(write_onlt=True)
	bread = serializers.IntegerField(read_only=True)
	bcomment = serializers.IntegerField(default=2, min_value=1, max_value=60)
	
	# 自定义单一字段验证
	def validate_btitle(self, value):
		if value == "python":
			raise serializers.ValidationError("书名python错误")
		return value
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • ② validate :多个字段验证
    在序列化器中需要同时对多个字段进行比较验证时,可以定义validate方法来验证,attrs即参与验证字段的值列表
calss BookSerializer(serializers.Serializaer):
	btitle = serializers.CharField(max_length=11, min_length=5)
	bpub_date = serializers.DateField(write_onlt=True)
	bread = serializers.IntegerField(read_only=True)
	bcomment = serializers.IntegerField(default=2, min_value=1, max_value=60)
	
	# 自定义多个字段验证
	def validate(self, attrs):
		if attrs["btitle"] == "java":
			raise serializers.ValidationError("书名java错误")
		return attrs
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 3 保存
    在验证成功后,想要完成对数据对象的创建,可以通过实现create()和update()两个方法来实现。
from rest_framework import serializers


calss BookSerializer(serializers.Serializaer):
	btitle = serializers.CharField(max_length=11, min_length=5)
	bpub_date = serializers.DateField(write_onlt=True)
	bread = serializers.IntegerField(read_only=True)
	bcomment = serializers.IntegerField(default=2, min_value=1, max_value=60)
	
	# 自定义单一字段验证
	def validate_btitle(self, value):
		if value == "python":
			raise serializers.ValidationError("书名python错误")
		return value
	# 自定义多个字段验证
	def validate(self, attrs):
		if attrs["btitle"] == "java":
			raise serializers.ValidationError("书名java错误")
		return attrs
	
	# 保存数据
	def create(self, validated_data):
		book = BookInfo.objects.create(btitle=validated_data["btitle"],bpub_date=validated_data["bpub_date"])
		return book

	# 更新数据
	def update(self, instance, validated_data):
		# instance 要更新的数据对象
		instance.btitle = validated_data['btitle']
		instance.save()
		return instance
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

视图 views.py

from django.views import View
from django.http import JsonResponse
from book.models import BookInfo
import json


class BooksView(View):
		
	def post(self,request):
		"""保存图书"""
		# 1、获取前端数据
		data = request.body.decode()
		data_dict = json.loads(data)
		# btitle = data_dict.get("btitle")
		# bpub_date = data_dict.get("bpub_date")
		
		# 2、验证数据
		ser = BookSerializer(data=data_dict)
		ser.is_valid(raise_exception=True)
		
		# 3、保存数据
		# book  BookInfo.objects.create(btitle=ser.validated_data['btitle'],
		#								bpub_date=ser.validated_data["bpub_date"])
		ser.save()
		# 4、返回结果
		return JsonResponse(ser.data)

class BookView(View):
	def put(self,request,pk):
		"""修改一本图书"""
		# 1、获取数据
		data = request.body.decode()
		data_dict = json.loads(data)

		# 2、校验数据
		book=BookInfo.objects.get(id=pk)
		ser = BookSerializer(book, data=data_dict)
		ser.isvalid(raise_exception=True)
		# 3、更新数据
		ser.save()
		# 4、返回结果
		return JsonResponse(ser.data)


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

说明:

  • 实现create()和update()两个方法后,在反序列化数据的时候,通过save()方法进行调用,返回一个数据对象的实例。
  • 如果创建序列化器对象的时候,没有传递instance实例,则调用save()方法的时候,create()被调用;如果传递了instance实例,则调用save()方法的时候,update()被调用。

4.模型类序列化器ModelSerializer

如果我们想要使用序列化器对应的是Django的模型类,DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类。
ModelSerializer与常规的Serializer相同,但提供了:
① 基于模型类自动生成一系列字段
② 基于模型类自动为Serializer生成validators,比如unique_together
③ 包含默认的create()和update()的实现

  • 1 定义
    说明:
    • model 指明参照哪个模型类
    • fields 指明为模型类的哪些字段生成
class BookModelSerializer(serializers.ModelSerializer):
	"""图书数据序列化器"""
	class Meta:
		model = BookInfo
		fields = '__all__'
  • 1
  • 2
  • 3
  • 4
  • 5

views.py

from django.views import View
from django.http import JsonResponse
from book.models import BookInfo
from book.serializers import BookModelSerializer
import json


class BooksView(View):

	def get(self,request):
		"""获取所有图书""""
		books = BookInfo.objects.all()
		ser = BookModelSerializer(books, many=True)
		return JsonResponse(ser.data, safe=False)


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 2 指定字段

① 使用fields来明确字段,__all__表名包含所有字段,也可以写明具体哪些字段

class BookModelSerializer(serializers.ModelSerializer):
	"""图书数据序列化器"""
	class Meta:
		model = BookInfo
		fields = ("id", "btitle")
  • 1
  • 2
  • 3
  • 4
  • 5

② 使用exclude可以明确排除掉哪些字段

class BookModelSerializer(serializers.ModelSerializer):
	"""图书数据序列化器"""
	class Meta:
		model = BookInfo
		exclude = ("id","btitle",)
  • 1
  • 2
  • 3
  • 4
  • 5

③ 指明只读字段

class BookModelSerializer(serializers.ModelSerializer):
	"""图书数据序列化器"""
	class Meta:
		model = BookInfo
		fields = '__all__'
		read_only_fields = ("bread",)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

④ 显示指明字段

class BookModelSerializer(serializers.ModelSerializer):
	"""图书数据序列化器"""
	# 显示指明字段
	bcomment = serializers.IntegerField(max_value=1000,min_value=50)
	class Meta:
		model = BookInfo
		fields = '__all__'
		read_only_fields = ("bread",)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 3 添加额外参数
    可以使用extra_kwargs参数为ModelSerializer添加或修改原有的选项参数
class BookModelSerializer(serializers.ModelSerializer):
	"""图书数据序列化器"""
	bcomment = serializers.IntegerField(max_value=1000,min_value=50)
	class Meta:
		model = BookInfo
		fields = '__all__'
		read_only_fields = ("bread",)
	
	# 使用extra_kwargs参数为is_delete字段添加默认值选项
	extra_kwargs={"is_delete":{"default":True}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

四、视图

1.Request与Response

  • 1.Request
    from rest_framework.request import Request
    REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的拓展了HttpRequest类的Request类的对象。
    REST framework 提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型将请求数据进行parse解析,解析为类字典对象保存到Request对象中。
    Request对象的数据是自动根据前端发送数据的格式进行解析之后的结果。
    常用属性:

    • 1.data
      request.data返回解析之后的请求体数据,与Django中标准的request.POST 和 request.FILES属性,但提供如下特性:
      • 包含了解析之后的文件和非文件数据
      • 包含了对POST、PUT、PATCH请求方式解析后的数据
      • 利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据
    • 2.query_params
      request.query_params 与Django标准的request.GET相同
  • Response
    from rest_framework.response import Response
    REST framework提供了一个响应类Response,使用该类构造响应对象时,响应的具体数据内容会被转换成符合前端需求的类型。

2.视图概览

3.视图说明

3.1 两个基类

  • 1.APIView
    rest_framework.views.APIView
    APIView是framework提供的所有视图的基类,继承自Django的View父类
    APIView与View的不同之处在于

    • 传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequest对象;
    • 视图方法可以返回REST framework的Response对象,视图会为响应数据设置(render)符合前端要求的格式;
    • 任何APIException异常都会被捕获到,并且处理成合适的响应信息;
    • 在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。

    支持定义的属性

    • authentication_classes 列表或元组,身份认证类
    • permission_classes 列表或元组,权限检查类
    • throttle_classes 列表或元组,流量控制类
from book.models import BookInfo
from book.serializers import BookSerializer
from rest_framework.response import Response
from rest_framework.views import APIView


class BooksView(APIView):
	def get(self,request):
		"""获取所有图书"""
		books = BookInfo.objects.all()
		ser = BookSerializer(books, many=True)
		return Response(ser.data)		
	
	def post(self,request):
		"""保存图书"""
		# 1、获取前端数据
		data = request.data
		# 2、验证数据
		ser = BookSerializer(data=data)
		ser.is_valid(raise_exception=True)
		# 3、保存数据
		ser.save()
		# 4、返回结果
		return Response(ser.data)

class BookView(APIView):
	def get(self, request, pk):
		"""获取单一图书"""
		# 查询数据
		try:
			hero = BookInfo.objects.get(id=pk)
		except:
			return Response({"error":"错误的id"}, status=400)
		# 返回结果
		ser = BookSerializer(book)
		return Response(ser.data)
		
	def put(self,request,pk):
		"""修改一本图书"""
		# 1、获取数据
		data = request.data
		# 2、校验数据
		book=BookInfo.objects.get(id=pk)
		ser = BookSerializer(book, data=data)
		ser.isvalid(raise_exception=True)
		# 3、更新数据
		ser.save()
		# 4、返回结果
		return Response(ser.data)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 2.GenericAPIView
    rest_framework.generics.CenericAPIView
    继承自APIView,增加了对于列表视图和详情视图可能用到的通用支持方法。通常使用时,可搭配一个或多个Mixin拓展类。
    支持定义的属性:
    • 列表视图与详情视图通用:
      • queryset 视图的查询集
      • serializer_class 视图使用的序列化器
    • 列表视图使用:
      • pagination_class 分页控制类
      • filter_backends 过滤控制后端
    • 详情页视图使用:
      • lookup_field 查询单一数据库对象时使用的条件字段,默认为pk
      • lookup_url_kwarg 查询单一数据时url中的参数关键字名称,默认与look_field相同
        提供的方法:
    • 列表视图与详情视图通用:
      • get_queryset()
        返回视图使用的查询集,默认返回queryset属性,可以重写
      • get_serializer_class()
        返回序列化器类,默认返回serializer_class,可以重写
      • get_serializer()
        返回序列化器对象,被其他视图或拓展类使用,如果在视图中想要获取序列化器对象,可以直接调用此方法。
    • 详情页视图使用:
      • get_object()
        返回详情视图所需的模型类数据对象,默认使用lookup_field参数来过滤queryset。在视图中可以调用该方法获取详情信息的模型类对象。
from book.models import BookInfo
from book.serializers import BookSerializer
from rest_framework.response import Response
from rest_framework.views import GenericAPIView


class BooksView(GenericAPIView):

	serializer_class = BookSerializer  # 指定序列化器
	queryset = BookInfo.objects.all()  # 指定数据对象查询集
	
	def get(self,request):
		"""获取所有图书"""
		books = self.get_queryset()  # 获取查询集所有数据
		ser = self.get_serializer(books, many=True)  # 获取序列化器对象
		return Response(ser.data)		
	
	def post(self,request):
		"""保存图书"""
		# 1、获取前端数据
		data = request.data
		# 2、验证数据
		ser = self.get_serializer(data=data)
		ser.is_valid(raise_exception=True)
		# 3、保存数据
		ser.save()
		# 4、返回结果
		return Response(ser.data)

class BookView(GenericAPIView):
	
	serializer_class = BookSerializer  # 指定序列化器
	queryset = BookInfo.objects.all()  # 指定数据对象查询集
	
	def get(self, request, pk):
		"""获取单一图书"""
		# 查询数据
		try:
			book = self.get_object()  # 从查询集中获取单一数据对象
		except:
			return Response({"error":"错误的id"}, status=400)
		# 返回结果
		ser = self.get_serializer(book)
		return Response(ser.data)
		
	def put(self,request,pk):
		"""修改一本图书"""
		# 1、获取数据
		data = request.data
		# 2、校验数据
		book= self.get_object()
		ser = self.get_serializer(book, data=data)
		ser.isvalid(raise_exception=True)
		# 3、更新数据
		ser.save()
		# 4、返回结果
		return Response(ser.data)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

3.2 五个拓展类

  • 1.ListModelMixin
    列表视图拓展类,提供list(request, *args, **kwargs)方法快速实现列表视图,该list()方法会对数据进行过滤和分页。成功返回200状态码

  • 2.CreateModelMixin
    创建视图拓展类,提供create(request, *args, **kwargs)方法快速实现创建资源的视图,成功返回201状态码,失败返回400错误。

  • 3.RetrieveModelMixin
    详情视图拓展类,提供retrieve(request, *args, **kwargs)方法,可以快速实现返回一个存在的数据对象。如果存在,返回200;否则返回404。

  • 4.UpdateModelMixin
    更新视图拓展类,提供update(request, *args, **kwargs)方法,可以快速实现更新一个存在的数据对象。成功返回200,失败返回400。

  • 5.DestoryModelMixin
    删除视图拓展类,提供destroy(request, *args, **kwargs)方法,可以快速实现删除一个存在的数据对象。成功返回204,不存在返回404。

from book.models import BookInfo
from book.serializers import BookSerializer
from rest_framework.response import Response
from rest_framework.views import GenericAPIView
from rest_framework.mixins import CreateModelMixin, UpdateModelMixin, DestroyModelMixin, RetrieveModelMixin, ListModelMixin


class BooksView(GenericAPIView,CreateModelMixin, ListModelMixin):
    serializer_class = BookSerializer  # 指定序列化器
    queryset = BookInfo.objects.all()  # 指定数据对象查询集

    def get(self, request):
        """获取所有图书"""
        return self.list(request)

    def post(self, request):
        """保存图书"""
        return self.create(request)


class BookView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin):
    serializer_class = BookSerializer  # 指定序列化器
    queryset = BookInfo.objects.all()  # 指定数据对象查询集

    def get(self, request, pk):
        """获取单一图书"""
        return self.retrieve(request, pk)

    def put(self, request, pk):
        """修改一本图书"""
        return self.update(request, pk)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
总结:
	调用流程:前端发起请求--->自己定义的类视图--->拓展类方法--->GenericAPIView方法
  • 1
  • 2

3.3 拓展类子类视图

  • CreateAPIView
    提供post方法,继承自GenericAPIView、CreateModelMixin
  • ListAPIView
    提供get方法,继承自GenericAPIView、ListModelMixin
  • RetrieveAPIView
    提供get方法,继承自GenericAPIView、RetriModelMixin
  • DestroyAPIView
    提供了delete方法,继承自GenericAPIView、DestroyModelMixin
  • UpdateAPIView
    提供put和patch方法,继承自GenericAPIView、UpdateModelMixin
  • RetrieveUpdateAPIView
    提供get、put、patch方法,继承自 GenericAPIView、RetrieveModelMixin、UpdateModelMixin
  • RetrieveUpdateDestoryAPIView
    提供 get、put、patch、delete方法,继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
  • ListCreateAPIView
  • RetrieveDestroyAPIView
from rest_framework.generics import CreateAPIView, ListAPIView, RetrieveAPIView, DestroyAPIView, UpdateAPIView, ListCreateAPIView, RetrieveDestroyAPIView, RetrieveUpdateDestroyAPIView, RetrieveUpdateAPIView
from book.models import BookInfo
from book.serializers import BookSerializer


class BooksView(ListCreateAPIView):
    serializer_class = BookSerializer  # 指定序列化器
    queryset = BookInfo.objects.all()  # 指定数据对象查询集


class BookView(RetrieveUpdateAPIView):
    serializer_class = BookSerializer  # 指定序列化器
    queryset = BookInfo.objects.all()  # 指定数据对象查询集
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

4.视图集

1、两个基本的视图集:

  • ViewSet
    继承自APIView
urlpatterns = {
    # 视图集路由匹配 ViewSet
    url(r'^books$', BooksViewSet.as_view({"get": "list", "post": "create"})),
    url(r'^books/last/$', BooksViewSet.as_view({"get": "last"})),
    url(r'^books/(?P<pk>\d+)/show/$',BooksViewSet.as_view({"get": "show"})),
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
from rest_framework.viewsets import ViewSet
from book.models import BookInfo
from books.serializers import BookSerializer
from rest_framework.response import Response


class BooksViewSet(ViewSet):
    def list(self, request):
        """返回所有图书数据"""
        books = BookInfo.objects.all()
        ser = BookSerializer(books, many=True)
        return Response(ser.data)

    def last(self, request):
        """返回一本图书信息"""
        book = BookInfo.objects.get(id=10)
        ser = BookSerializer(book)
        return Response(ser.data)

    def show(self, request, pk):
        """返回前端指定的图书信息"""
        book = BookInfo.objects.get(id=pk)
        ser = BookSerializer(book)
        return Response(ser.data)

    def create(self, request):
        """保存图书数据"""
        data = request.data
        ser = BookSerializer(data=data)
        ser.is_valid(raise_exception=True)
        ser.save()
        return Response(ser.data)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • GenericViewSet
    继承自GenericAPIView
urlpatterns = {
    # 视图集路由匹配 GenericViewSet
    url(r'^books$', BooksGenericViewSet.as_view({"get": "list", "post": "create"})),
    url(r'^books/last/$', BooksGenericViewSet.as_view({"get": "last"})),
    url(r'^books/(?P<pk>\d+)/show/$', BooksGenericViewSet.as_view({"get": "show"})),
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
class BooksGenericViewSet(GenericViewSet):
    serializer_class = BookSerializer
    queryset = BookInfo.objects.all()

    def list(self, request):
        """返回所有图书数据"""
        books = self.get_queryset()
        ser = self.get_serializer(books, many=True)
        return Response(ser.data)

    def last(self, request):
        """返回一本图书信息"""
        book = BookInfo.objects.get(id=10)
        ser = self.get_serializer(book)
        return Response(ser.data)

    def show(self, request, pk):
        """返回前端指定的图书信息"""
        book = self.get_object()
        ser = self.get_serializer(book)
        return Response(ser.data)

    def create(self, request):
        """保存图书数据"""
        data = request.data
        ser = self.get_serializer(data=data)
        ser.is_valid(raise_exception=True)
        ser.save()
        return Response(ser.data)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

2、两个拓展视图集

  • ModelViewSew
    继承自GenericViewSet和五个拓展类
urlpatterns = {
    # 视图集路由匹配 ModelViewSet
    url(r'^books$', BooksModelViewSet.as_view({"get": "list", "post": "create"})),
    url(r'^books/(?P<pk>\d+)/$', BooksModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
    url(r'^books/(?P<pk>\d+)/show/$', BooksModelViewSet.as_view({"get": "show"})),
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
from rest_framework.viewsets import  ModelViewSet
from book.models import BookInfo
from books.serializers import BookSerializer
from rest_framework.response import Response

class BooksModelViewSet(ModelViewSet):
    serializer_class = BookSerializer
    queryset = BookInfo.objects.all()

    # 自定义方法
    def show(self, request, pk):
        book = self.get_object()
        ser = self.get_serializer(book)
        return Response(ser.data)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • ReadOnlyModelViewSet
    继承自GenericViewSet、ListModelMixin、RetrieveModelMixin
    示例参照ModelViewSet代码,区别仅在于ReadOnlyModelViewSet仅实现封装了查询方法

3、action属性
在视图集中,我们可以通过action对象属性来获取当前请求视图集时的action动作是哪一个。

class BooksModelViewSet(ModelViewSet):
    # serializer_class = BookSerializer
    queryset = BookInfo.objects.all()
    
    # 对于不同的方法使用不同的序列化器
    def get_serializer_class(self):
        if self.action == "list":
            return BookSerializer
        elif self.action == "create":
            return BookSerializer1
        else:
            return BookSerializer2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

5.路由Router

对于视图集ViewSet,我们除了可以自己手动指明请求方式与动作之间的对应关系外,还可以使用Routers快速实现路由信息。
REST framework提供了两个router

  • SimpleRouter
  • DefaultRouter

DefaultRouter与SimpleRouter的区别:DefaultRouter继承自SimpleRouter,多提供了一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。
1、使用方法:

  • 创建router对象,并注册视图集
from rest_framework import routers

router = routers.SimpleRouter()
router.register(prefix, viewset, base_name)
  • 1
  • 2
  • 3
  • 4
说明:router.register(prefix, viewset, base_name)
 prefix 该视图集的路由前缀
 viewset  视图集
 base_name  路由名称的前缀
  • 1
  • 2
  • 3
  • 4
  • 添加路由数据
    有2种方式

第一种方式:

urlpatterns = [
	...
]
urlpatterns += router.urls
  • 1
  • 2
  • 3
  • 4

第二种方式:

urlpatterns = [
	...
	url(r'^', include(router.urls))
]
  • 1
  • 2
  • 3
  • 4

示例:

# 自动生成路由的使用
from rest_framework.routers import SimpleRouter

urlpatterns = [

]
router = SimpleRouter()
router.register("books", BooksModelViewSet, base_name='book')
urlpatterns += router.urls





from rest_framework.viewsets import ViewSet, GenericViewSet, ModelViewSet
from book.models import BookInfo
from books.serializers import BookSerializer
from rest_framework.response import Response

class BooksModelViewSet(ModelViewSet):
    # serializer_class = BookSerializer
    queryset = BookInfo.objects.all()

    # 对于不同的方法使用不同的序列化器
    def get_serializer_class(self):
        if self.action == "list":
            return BookSerializer
        elif self.action == "create":
            return BookSerializer1
        else:
            return BookSerializer2

    def show(self, request, pk):
        book = self.get_object()
        ser = self.get_serializer(book)
        return Response(ser.data)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
此视图集会形成的路由:
[<RegexURLPattern book-list ^books/$>,<RegexURLPattern book-detail ^books/(?P<pk>[ ^/.]+)/ $>]
  • 1
  • 2

2、视图集中包含附加action装饰器的
对于自定义方法,自动生成路由,需要使用action装饰器
rest_framework.decorators.action
action装饰器接收两个参数:

  • methods:该action支持的请求方式,列表
  • detail:表示的是action中要处理的是否是视图资源的对象(即是否通过url路径获取主键)
    • True 表示使用通过URL获取主键对应的数据对象(生成正则匹配)
    • False 表示不使用URL获取主键(不会生成正则匹配)

示例:

# 自动生成路由的使用
from rest_framework.routers import SimpleRouter

urlpatterns = [

]
router = SimpleRouter()
router.register("books", BooksModelViewSet, base_name='book')
urlpatterns += router.urls





from rest_framework.viewsets import ViewSet, GenericViewSet, ModelViewSet
from book.models import BookInfo
from books.serializers import BookSerializer
from rest_framework.response import Response
from rest_framework.decorators import action


class BooksModelViewSet(ModelViewSet):
    # serializer_class = BookSerializer
    queryset = BookInfo.objects.all()

    # 对于不同的方法使用不同的序列化器
    def get_serializer_class(self):
        if self.action == "list":
            return BookSerializer
        elif self.action == "create":
            return BookSerializer1
        else:
            return BookSerializer2

    # 自定义方法
    @action(methods=["get"], detail=True)
    def show(self, request, pk):
        book = self.get_object()
        ser = self.get_serializer(book)
        return Response(ser.data)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
此视图集会形成的路由:
[<RegexURLPattern book-list ^books/$>,<RegexURLPattern book-detail ^books/(?P<pk>[ ^/.]+)/ $>,<RegexURLPattern book-show ^books/(?P<pk>[ ^/.]+)/show/ $>]
  • 1
  • 2

总结-视图集使用场景:

五、其他功能

1.认证 Authentication

1.1 全局:在配置文件settins.py文件中配置全局默认的认证方案

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',   # 基本认证
        'rest_framework.authentication.SessionAuthentication',  # session认证
    )
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

1.2 局部:在每个视图中通过authentication_classes属性设置

from rest_framework.viewsets import  ModelViewSet
from book.models import BookInfo
from books.serializers import BookSerializer
from rest_framework.authentication import SessionAuthentication, BaseAuthentication

class BooksModelViewSet(ModelViewSet):
    serializer_class = BookSerializer
    queryset = BookInfo.objects.all()

    # 认证
    authentication_classes = [BaseAuthentication, SessionAuthentication]

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

认证失败会有两种可能的返回值:

  • 401 Unauthorized 未认证
  • 403 Permission Denied 权限被禁止

2.权限 Permissions

权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。

  • 在执行视图的dispatch()方法前,会先进行视图访问权限的判断。
  • 在通过get_object()获取具体对象时,会进行对象权限的判断。

2.1 全局:在配置文件中设置默认的权限管理类

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',  # 仅通过认证的用户 
    )
}
  • 1
  • 2
  • 3
  • 4
  • 5

如果未指明,则采用如下配置

REST_FRAMEWORK = {
	'DEFAULT_PERMISSION_CLASSES': (
   		'rest_framework.permissions.AllowAny',  # 允许所有用户
	)
}
  • 1
  • 2
  • 3
  • 4
  • 5

2.2 局部:在具体的视图中通过permission_classes属性设置

from rest_framework.viewsets import  ModelViewSet
from book.models import BookInfo
from books.serializers import BookSerializer
from rest_framework.authentication import SessionAuthentication, BaseAuthentication
from rest_framework.permissions import IsAuthenticated

class BooksModelViewSet(ModelViewSet):
    serializer_class = BookSerializer
    queryset = BookInfo.objects.all()

    # 认证
    authentication_classes = [BaseAuthentication, SessionAuthentication]
    # 权限
    permission_classes = [IsAuthenticated]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

提供的权限:

  • AllowAny 允许所有用户
  • IsAuthenticated 仅通过认证的用户
  • IsAdminUser 仅管理员用户
  • IsAuthenticatedOrReadOnly 认证的用户可以完全操作,否则只能get读取

3.限流 Throttling

可以对接口访问的频次进行限制,以减轻服务器压力。
3.1 全局:配置文件中,使用DEFAULT_THROTTLE_CLASSES 和 DEFAULT_THROTTLE_RATES进行全局配置

REST_FRAMEWORK = {
	# 用户划分限流
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ),
    # 指定不同用户访问次数,可以使用 second, minute, hour 或day来指明周期。
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/day'
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3.2 局部:在具体视图中通过throttle_classes属性来配置

from rest_framework.viewsets import  ModelViewSet
from book.models import BookInfo
from books.serializers import BookSerializer
from rest_framework.authentication import SessionAuthentication, BaseAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.throttling import UserRateThrottle

class BooksModelViewSet(ModelViewSet):
    serializer_class = BookSerializer
    queryset = BookInfo.objects.all()

    # 认证
    authentication_classes = [BaseAuthentication, SessionAuthentication]
    # 权限
    permission_classes = [IsAuthenticated]
    # 限流
    throttle_classes = [UserRateThrottle]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

可选限流类:

  • AnonRateThrottle
    限制所有匿名未认证用户,使用IP区分用户。
    使用DEFAULT_THROTTLE_RATES[‘anon’] 来设置频次
  • UserRateThrottle
    限制认证用户,使用User id 来区分。
    使用DEFAULT_THROTTLE_RATES[‘user’] 来设置频次
  • ScopedRateThrottle
    限制用户对于每个视图的访问频次,使用ip或user id。

ScopedRateThrottle 示例:

class ContactListView(APIView):
    throttle_scope = 'contacts'
    ...

class ContactDetailView(APIView):
    throttle_scope = 'contacts'
    ...

class UploadView(APIView):
    throttle_scope = 'uploads'
    ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.ScopedRateThrottle',
    ),
    'DEFAULT_THROTTLE_RATES': {
        'contacts': '1000/day',
        'uploads': '20/day'
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

4.过滤 Filtering

对于列表数据可能需要根据字段进行过滤,可以通过添加django-filter扩展增强支持。

  • 安装django-filter
pip install django-filter
  • 1
  • 在配置文件settings.py文件中添加注册应用
INSTALLED_APPS = [
    ...
    'django_filters',  # 需要注册应用,
]
  • 1
  • 2
  • 3
  • 4
  • 在配置文件settings.py中增加过滤后端的设置
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}
  • 1
  • 2
  • 3
  • 在视图中添加filter_fields属性,指定可以过滤的字段
from rest_framework.viewsets import  ModelViewSet
from book.models import BookInfo
from books.serializers import BookSerializer
from rest_framework.authentication import SessionAuthentication, BaseAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.throttling import UserRateThrottle

class BooksModelViewSet(ModelViewSet):
    serializer_class = BookSerializer
    queryset = BookInfo.objects.all()

    # 认证
    authentication_classes = [BaseAuthentication, SessionAuthentication]
    # 权限
    permission_classes = [IsAuthenticated]
    # 限流
    throttle_classes = [UserRateThrottle]
    # 过滤
    filter_fields = ["btitle", "bread"]
    # 127.0.0.1:8000/books/?btitle=西游记
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

5.排序 Ordering

对于列表数据,REST framework提供了OrderingFilter过滤器快速指明数据按照指定字段进行排序。

  • 在类视图中设置filter_backends,使用rest_framework.filters.OrderingFilter过滤器,REST framework会在请求的查询字符串参数中检查是否包含了ordering参数,如果包含了ordering参数,则按照ordering参数指明的排序字段对数据集进行排序。
  • 前端可以传递的ordering参数的可选字段值需要在ordering_fields中指明。
from rest_framework.viewsets import  ModelViewSet
from book.models import BookInfo
from books.serializers import BookSerializer
from rest_framework.authentication import SessionAuthentication, BaseAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.throttling import UserRateThrottle
from rest_framework.filters import OrderingFilter

class BooksModelViewSet(ModelViewSet):
    serializer_class = BookSerializer
    queryset = BookInfo.objects.all()

    # 认证
    authentication_classes = [BaseAuthentication, SessionAuthentication]
    # 权限
    permission_classes = [IsAuthenticated]
    # 限流
    throttle_classes = [UserRateThrottle]
   
    # 过滤
    # filter_fields = ["btitle", "bread"]
    # 127.0.0.1:8000/books/?btitle=西游记
   
    # 排序
    filter_backends = [OrderingFilter]
    ordering_fields = ["id", "bread"]
    # 127.0.0.1:8000/books/?ordering=-bread
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

6.分页 Pagination

6.1 全局:在配置文件中设置全局的分页方式

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS':  'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 100  # 每页数目
}
  • 1
  • 2
  • 3
  • 4

6.2局部:通过自定义Pagination类,来为视图添加不同分页行为。在视图中通过pagination_clas属性来指明。

from rest_framework.viewsets import  ModelViewSet
from book.models import BookInfo
from books.serializers import BookSerializer
from rest_framework.authentication import SessionAuthentication, BaseAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.throttling import UserRateThrottle
from rest_framework.filters import OrderingFilter
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination


# 自定义Pagination类
class PageNum(PageNumberPagination):
    page_size_query_param = 'page_size'  # 指定控制页面展示数量的参数
    max_page_size = 10  # 最大展示数量
    
class BooksModelViewSet(ModelViewSet):
    serializer_class = BookSerializer
    queryset = BookInfo.objects.all()
    authentication_classes = [BaseAuthentication, SessionAuthentication]
    permission_classes = [IsAuthenticated]
    throttle_classes = [UserRateThrottle]
    # filter_fields = ["btitle", "bread"]
    # 127.0.0.1:8000/books/?btitle=西游记
    filter_backends = [OrderingFilter]
    ordering_fields = ["id", "bread"]
    # 127.0.0.1:8000/books/?ordering=-bread

	# 分页
	pagination_class = PageNum
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

6.3 可选分页器

  • PageNumberPagination
    前端访问网址形式:
    127.0.0.1/books/?page=1&page_size=2
    可以在子类中定义的属性:
    • page_size 每页数目
    • page_query_param 前端发送的页数关键字名,默认为page
    • page_size_query_param 前端发送的每页数目关键字名,默认为None
    • max_page_size 前端最多能设置的每页数量
  • LimitOffsetPagination
    前端访问网址形式:
    127.0.0.1:8000/books/?offset=3&limit=2
    可以在子类中定义的属性:
    • default_limit 默认限制,默认值与PAGE_SIZE设置一致
    • limit_query_param limit参数名,默认’limit’
    • offset_query_param offset参数名,默认’offset’
    • max_limit 最大limit限制,默认None

7.异常处理 Exceptions

REST framework提供了异常处理,我们可以自定义异常处理函数。
在配置文件中声明自定义的异常处理

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}
  • 1
  • 2
  • 3

示例:处理关于数据库的异常

from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework import status
from django.db import DatabaseError

def exception_handler(exc, context):
    response = drf_exception_handler(exc, context)

    if response is None:
        view = context['view']
        if isinstance(exc, DatabaseError):
            print('[%s]: %s' % (view, exc))
            response = Response({'detail': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)

    return response
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

REST framework定义的异常:

  • APIException 所有异常的父类
  • ParseError 解析错误
  • AuthenticationFailed 认证失败
  • NotAuthenticated 尚未认证
  • PermissionDenied 权限决绝
  • NotFound 未找到
  • MethodNotAllowed 请求方式不支持
  • NotAcceptable 要获取的数据格式不支持
  • Throttled 超过限流次数
  • ValidationError 校验失败

8.自动生成接口文档

REST framework可以自动帮助我们生成接口文档。接口文档以网页的方式呈现。自动接口文档能生成的是继承自APIView及其子类的视图。

  • 安装依赖
  • REST framewrok生成接口文档需要coreapi库的支持。
pip install coreapi
  • 1
  • 设置接口文档访问路径
    在总路由中添加接口文档路径。
    文档路由对应的视图配置为rest_framework.documentation.include_docs_urls,
    参数title为接口文档网站的标题。
from rest_framework.documentation import include_docs_urls

urlpatterns = [
    ...
    url(r'^docs/', include_docs_urls(title='My API title'))
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 文档描述说明的定义位置

1)单一方法的视图,可直接使用类视图的文档字符串

class BookListView(generics.ListAPIView):
    """
    返回所有图书信息.
    """
  • 1
  • 2
  • 3
  • 4

2) 包含多个方法的视图,在类视图的文档字符串中,分开方法定义

class BookListCreateView(generics.ListCreateAPIView):
    """
    get:
    返回所有图书信息.

    post:
    新建图书.
    """
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3) 对于视图集ViewSet,仍在类视图的文档字符串中分开定义,但是应使用action名称区分

class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
    """
    list:
    返回图书列表数据

    retrieve:
    返回图书详情数据

    latest:
    返回最新的图书数据

    read:
    修改图书的阅读量
    """
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 访问接口文档网页
    浏览器访问 127.0.0.1:8000/docs/,即可看到自动生成的接口文档。
    在这里插入图片描述
    说明:
    • 视图集ViewSet中的retrieve名称,在接口文档网站中叫做read
    • 参数的Description需要在模型类或序列化器类的字段中以help_text选项定义,如:
class BookInfo(models.Model):
    ...
    bread = models.IntegerField(default=0, verbose_name='阅读量', help_text='阅读量')
    ...
  • 1
  • 2
  • 3
  • 4
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/479527
推荐阅读
相关标签
  

闽ICP备14008679号