概念

将数据库的东西通过ORM的映射取出来,通过view和serializers文件绑定REST接口,当前端请求时,返回序列化好的json。利用DRF框架快速的实现RestAPI接口的设计,大大提高RestAPI接口开发效率。
DRF是Django的超集,去掉了模板的部分,提供了一个REST的接口,同时也提供了满足该接口的代码工作流。同时,在REST的规范下,升级了权限和分页等功能,增加了限流和过滤搜索等功能。

安装、配置

pip install django-rest-framework
INSTALLED_APPS = [
······
    'rest_framework',
]

RestAPI接口

  • REST
    Representational State Transfer: 资源的表现层状态转换

    一切皆是资源
    每一个资源的每一个状态都应该有唯一的操作方式

    • POST 增加资源
    • GET 请求资源
    • PUT 修改资源
    • DELETE 删除资源
  • RestAPI接口核心工作
    1.将数据库数据序列化为前端所需要的格式,并返回
    2.将前端发送的数据反序列化为模型类对象,并保存到数据库中

序列化器

Serializer 功能:进行数据的序列化和反序列化,继承自serializers.Serializer

创建序列化器

app目录下新建一个文件serializers.py,因为是做序列化,而序列化是针对模型的,所以需要跟模型文件models.py在同一个目录下

1
2
3
4
class BookInfoSerializer(serializers.ModelSerializer):
class Meta:
model = BookInfo
fields = '__all__'

model 指明该序列化器处理的数据字段从模型类BookInfo参考生成
fields 指明该序列化器包含模型类中的哪些字段,’all‘指明包含所有字段

** 定义视图路由 **

1
2
3
router = DefaultRouter()  # 可以处理视图的路由器
router.register(r'books', views.BookInfoViewSet) # 向路由器中注册视图集
urlpatterns += router.urls # 将路由器中的所以路由信息追到到django的路由列表中

序列化

1)序列化单个对象

1
2
3
book = BookInfo.objects.get(id=1)
serializer = BookInfoSerializer(book)
res = json.dumps(serializer.data,ensure_ascii=False,indent=4)

2)序列化多个对象,其实就是添加了一个many参数

1
2
3
books = BookInfo.objects.all()
serializer = BookInfoSerializer(books,many=True)
res = json.dumps(serializer.data,ensure_ascii=False,indent=4)

3)关联对象的嵌套序列化

1.将关联对象序列化为关联对象的主键

1
hbook = serializers.PrimaryKeyRelatedField(label='图书',read_only=True)

2.采用指定的序列化器将关联对象进行序列化

1
hbook = BookInfoSerializer(label='图书')   

3.将关联对象序列化为关联对象模型类_str_方法的返回值

1
hbook = serializers.StringRelatedField(label='图书')

转换成标准Json数据的另一种方式:JSONRenderer().render(serializer.data)

反序列化

1
2
3
4
data = JSONRenderer().parse(content)
serializer = IdcSerializer(data=data)
serializer.is_valid()
serializer.save()

使用序列化器改写RestAPI接口

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
class BookListView(View):
def get(self, request):
"""
获取所有的图书的信息:
1. 查询所有的图书的数据
2. 返回所有图书的json的数据
"""
# 1. 查询所有的图书的数据
books = BookInfo.objects.all() # QuerySet

# 2. 返回所有图书的json的数据,状态码: 200
books_li = []
for book in books:
# 将book对象转换成dict
book_dict = {
'id': book.id,
'btitle': book.btitle,
'bpub_date': book.bpub_date,
'bread': book.bread,
'bcomment': book.bcomment,
'image': book.image.url if book.image else ''
}

books_li.append(book_dict)

return JsonResponse(books_li, safe=False)

def post(self, request):
"""
新增一本图书的信息:
1. 获取参数btitle和bpub_date并进行校验
2. 创建图书信息并添加进数据表中
3. 返回新增的图书的json数据,状态码: 201
"""
# 需求: 前端需要传递新增图书的信息(btitle, bpub_date),通过json传递

# 1. 获取参数btitle和bpub_date并进行校验
# 获取json的原始数据
req_data = request.body # bytes

# 将bytes转换为str
json_str = req_data.decode()

# 将json字符串转换dict
req_dict = json.loads(json_str)

# 获取btitle和bpub_date
btitle = req_dict.get('btitle')
bpub_date = req_dict.get('bpub_date')

# TODO: 省略参数校验过程...

# 2. 创建图书信息并添加进数据表中
book = BookInfo.objects.create(
btitle=btitle,
bpub_date=bpub_date
)

# 3. 返回新增的图书的json数据,状态码: 201
# 将book对象转换成dict
book_dict = {
'id': book.id,
'btitle': book.btitle,
'bpub_date': book.bpub_date,
'bread': book.bread,
'bcomment': book.bcomment,
'image': book.image.url if book.image else ''
}

return JsonResponse(book_dict, status=201)

修改后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class BookListView(View):
def get(self, request):
books = BookInfo.objects.all() # QuerySet
serializer = BookInfoSerializer(books, many=True)
return JsonResponse(serializer.data, safe=False)

def post(self, request):
req_data = request.body # bytes
json_str = req_data.decode()
req_dict = json.loads(json_str)

serializer = BookInfoSerializer(data=req_dict)
serializer.is_valid(raise_exception=True)

# 反序列化-数据保存(新增) -> create
serializer.save()
return JsonResponse(serializer.data, status=201)

编写序列化验证、创建、更新、保存规则:

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
from rest_framework import serializers
from .models import Idc # 需要导入Idc模型

class IdcSerializer(serializers.Serializer):
"""
Idc, 序列化类
"""
id = serializers.IntegerField(read_only=True) # 处理只读
name = serializers.CharField(required=True, max_length=32) # 字段必须传, 最大限制32个字符
address = serializers.CharField(required=True, max_length=256)
phone = serializers.CharField(required=True, max_length=15)
email = serializers.CharField(required=True)
letter = serializers.CharField(required=True, max_length=5)


def create(self, validated_data):
return Idc.objects.create(**validated_data) # 调用Idc模型进行create操作

def update(self, instance, validated_data): ''' 参数介绍:instance是当前的对象,
validated_data是处理过的干净数据'''
'''
update方法可以允许修改什么字段,如果有不需要修改的字段,不写即可
'''
instance.name = validated_data.get("name",instance.name)
instance.address = validated_data.get("address",instance.name)
instance.phone = validated_data.get("phone",instance.name)
instance.email = validated_data.get("email",instance.name)
instance.save()
return instance

在“正向序列化”的时候,CharField字段里的参数都没有作用,只有在“反向序列化”是才会有作用。
Django能自动判断你提交的请求是需要增加,判断的标准是基于ID,如果传入的数据里有ID的话,那么认为你是需要进行修改,没有ID则认为是需要进行创建

补充知识

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import subprocess
def run_cdoe(code):
try:
output = subprocess.check_output(
# 用 python 脚本来执行 python 字符串
['python','c',code],
# 自动将输出解码为字符串
universal_newlines=True,
# 控制错误输出流
stderr=subprocess.STDOUT,
# 控制客户端代码执行时间
timeout=30)
except subprocess.CalledProcessError as e:
output = e.output
except subprocess.TimeoutExpired as e:
output = '\r\n'.join(['Time Out!!!',e.output])
return output

code = """print('Test success')"""

跨域问题

模板表单:给表单加 {% csrf_tokne %} 
REST 架构,禁用 csrf 功能
1
2
3
4
5
6
7
8
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
@require_POST
def api(request):
code = request.POST.get('code')
output = run_code(code)
return JsonResponse(data={'output':output})

RESTful