序列化组件

序列化器-Serializer

  1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
  2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
  3. 反序列化,完成数据校验功能

1.定义序列化器

Django REST framework中的Serializer使用类来定义,须继承自rest_framework.serializers.Serializer。

接下来,为了方便演示序列化器的使用,我们先创建一个新的子应用sers

1
python manage.py startapp sers

settings.py注册sers

1
2
3
4
INSTALLED_APPS = [
....
'sers',
]

我们创建几个图书相关模型

1
2
3
4
5
6
7
from django.db import models
# Create your models here.

class Book(models.Model):
title = models.CharField(max_length=32,verbose_name="书籍名称")
price = models.IntegerField(verbose_name="价格")
pub_date = models.DateField(verbose_name="出版日期")

我们想为Book模型类提供一个序列化器,可以定义如下:

1
2
3
4
5
6
from rest_framework import serializers

class BookSerializer(serializers.Serializer):
title = serializers.CharField()
price = serializers.IntegerField()
pub_date = serializers.DateField()

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

常用字段类型

常用字段类型

字段字段构造方式
BooleanFieldBooleanField()
NullBooleanFieldNullBooleanField()
CharFieldCharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailFieldEmailField(max_length=None, min_length=None, allow_blank=False)
RegexFieldRegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugFieldSlugField(max_length=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9*-]+
URLFieldURLField(max_length=200, min_length=None, allow_blank=False)
UUIDFieldUUIDField(format=’hex_verbose’) format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3)'int' – 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
IPAddressFieldIPAddressField(protocol=’both’, unpack_ipv4=False, **options)
IntegerFieldIntegerField(max_value=None, min_value=None)
FloatFieldFloatField(max_value=None, min_value=None)
DecimalFieldDecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
DateTimeFieldDateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateFieldDateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeFieldTimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationFieldDurationField()
ChoiceFieldChoiceField(choices) choices与Django的用法相同
MultipleChoiceFieldMultipleChoiceField(choices)
FileFieldFileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageFieldImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListFieldListField(child=, min_length=None, max_length=None)
DictFieldDictField(child=)

选项参数:

参数名称作用
max_length最大长度
min_lenght最小长度
allow_blank是否允许为空
trim_whitespace是否截断空白字符
max_value最小值
min_value最大值

通用参数:

参数名称说明
read_only表明该字段仅用于序列化输出,默认False
write_only表明该字段仅用于反序列化输入,默认False
required表明该字段在反序列化时必须输入,默认True
default反序列化时使用的默认值
allow_null表明该字段是否允许传入None,默认False
validators该字段使用的验证器
error_messages包含错误编号与错误信息的字典
label用于HTML展示API页面时,显示的字段名称
help_text用于HTML展示API页面时,显示的字段帮助提示信息

2.创建Serializer对象

定义好Serializer类后,就可以创建Serializer对象了。

Serializer的构造方法为:

1
Serializer(instance=None, data=empty, **kwarg)

说明:

1)用于序列化时,将模型类对象传入instance参数

2)用于反序列化时,将要被反序列化的数据传入data参数

3)除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如

1
serializer = AccountSerializer(account, context={'request': request})

通过context参数附加的数据,可以通过Serializer对象的context属性获取。

  1. 使用序列化器的时候一定要注意,序列化器声明了以后,不会自动执行,需要我们在视图中进行调用才可以。
  2. 序列化器无法直接接收数据,需要我们在视图中创建序列化器对象时把使用的数据传递过来。
  3. 序列化器的字段声明类似于我们前面使用过的表单系统。
  4. 开发restful api时,序列化器会帮我们把模型数据转换成字典.
  5. drf提供的视图会帮我们把字典转换成json,或者把客户端发送过来的数据转换字典.

3.序列化器的使用

序列化器的使用分两个阶段:

  1. 在客户端请求时,使用序列化器可以完成对数据的反序列化。
  2. 在服务器响应时,使用序列化器可以完成对数据的序列化。

序列化

基本序列化使用

1) 先查询出一个图书对象

1
2
from sers.models import Book
book = Book.objects.get(pk=1)

2) 构造序列化器对象

1
2
from .serializers import BookSerializer
bookSer = BookSerializer(instance=book)

3)获取序列化数据

通过data属性可以获取序列化后的数据

1
2
bookSer.data 
# {'title': '乱世佳人', 'price': 335, 'pub_date': '2012-12-12'}

4)如果要被序列化的是包含多条数据的查询集QuerySet,可以通过添加many=True参数补充说明

1
2
3
4
5
6
7
class BookView(APIView):

def get(self, request):
# book = Book.objects.get(pk=1)
books = Book.objects.all()
bs = BookSerializer(instance=books, many=True)
return Response(bs.data)

反序列化

数据验证

使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。

在获取反序列化的数据前,必须调用**is_valid()**方法进行验证,验证成功返回True,否则返回False。

验证失败,可以通过序列化器对象的errors属性获取错误信息,返回字典,包含了字段和字段的错误。如果是非字段错误,可以通过修改REST framework配置中的NON_FIELD_ERRORS_KEY来控制错误字典中的键名。

验证成功,可以通过序列化器对象的validated_data属性获取数据。

在定义序列化器时,指明每个字段的序列化类型和选项参数,本身就是一种验证行为。

如我们前面定义过的BookSerializer

1
2
3
4
5
6
from rest_framework import serializers

class BookSerializer(serializers.Serializer):
title = serializers.CharField()
price = serializers.IntegerField()
pub_date = serializers.DateField()

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

1
2
3
4
5
6
7
8
from sers.sers import BookSerializer
bs = BookSerializer(data={"title":"小王子","price":100})
bs.is_valid() # 必须先要is_valid,才会有bs.validated_data和bs.errors
False
bs.validated_data
{}
bs.errors
{'pub_date': [ErrorDetail(string='This field is required.', code='required')]}

可以设置required=False让校验字段可以为空!

is_valid()方法还可以在验证失败时抛出异常serializers.ValidationError,可以通过传递raise_exception=True参数开启,REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应。

1
2
## Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)

如果觉得这些还不够,需要再补充定义验证行为,可以使用以下三种方法:

validate_字段名

<field_name>字段进行验证,如

1
2
3
4
5
6
7
8
9
class BookSerializer(serializers.Serializer):
title = serializers.CharField(max_length=32)
price = serializers.IntegerField(required=True)
pub_date = serializers.DateField(required=True)

def validate_title(self, value):
if 'django' not in value.lower():
raise serializers.ValidationError("图书不是关于Django的")
return value

测试

1
2
3
4
5
6
from sers.sers import BookSerializer
bs = BookSerializer(data={"title":"小王子","price":100})
bs.is_valid()
False
bs.errors
{'title': [ErrorDetail(string='图书不是关于Django的', code='invalid')], 'pub_date': [ErrorDetail(string='This field is required.', code='required')]}

还有一种写法:

1
2
3
4
5
6
7
8
def title_django(self, value):
if 'django' not in value.lower():
raise serializers.ValidationError("图书不是关于Django的")
return value

class BookSerializer(serializers.Serializer):
title = serializers.CharField(max_length=32,validators=[title_django,])
...
validate

在序列化器中需要同时对多个字段进行比较验证时,可以定义validate方法来验证,如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class BookSerializer(serializers.Serializer):
title = serializers.CharField(max_length=32)
price = serializers.IntegerField(required=False)
pub_date = serializers.DateField(required=False)

bread = serializers.IntegerField(label='阅读量', max_value=2147483647, min_value=-2147483648, required=False)
bcomment = serializers.IntegerField(label='评论量', max_value=2147483647, min_value=-2147483648, required=False)


def validate_title(self, value):
if 'django' not in value.lower():
raise serializers.ValidationError("图书不是关于Django的")
return value

def validate(self, data):
bread = data.get("bread")
bcomment = data.get("bcomment")
if bread < bcomment:
raise serializers.ValidationError('阅读量小于评论量')
return data

测试

1
2
3
4
5
bs = BookSerializer(data={"title":"Django深入浅出","bread":100,"bcomment":200,"publish_id":1})
bs.is_valid()
False
bs.errors
{'non_field_errors': [ErrorDetail(string='阅读量小于评论量', code='invalid')]}
validators

在字段中添加validators选项参数,也可以补充验证行为,如

1
2
3
4
5
6
7
8
def about_django(value):
if 'django' not in value.lower():
raise serializers.ValidationError("图书不是关于Django的")

class BookSerializer(serializers.Serializer):
title = serializers.CharField(max_length=32, validators=[about_django])
price = serializers.IntegerField(required=False)
pub_date = serializers.DateField(required=False)

测试:

1
2
3
4
5
6
from sers.sers import BookSerializer
data = {'btitle': 'python'}
serializer = BookSerializer(data=data)
serializer.is_valid() # False
serializer.errors
## {'btitle': [ErrorDetail(string='图书不是关于Django的', code='invalid')]}
反序列化-保存数据

前面的验证数据成功后,我们可以使用序列化器来完成数据反序列化的过程.这个过程可以把数据转成模型类对象.

可以通过实现create()和update()两个方法来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
...

def create(self, validated_data):
"""新建"""
return Book(**validated_data)

def update(self, instance, validated_data):
"""更新,instance为要更新的对象实例"""
instance.btitle = validated_data.get('btitle', instance.btitle)
instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
instance.bread = validated_data.get('bread', instance.bread)
instance.bcomment = validated_data.get('bcomment', instance.bcomment)
return instance

如果需要在返回数据对象的时候,也将数据保存到数据库中,则可以进行如下修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
...

def create(self, validated_data):
"""新建"""
return Book.objects.create(**validated_data)

def update(self, instance, validated_data):
"""更新,instance为要更新的对象实例"""
instance.title = validated_data.get('title', instance.title)
instance.price = validated_data.get('price', instance.price)
instance.pub_date = validated_data.get('pub_date', instance.pub_date)
instance.save()
return instance

实现了上述两个方法后,在反序列化数据的时候,就可以通过save()方法返回一个数据对象实例了

1
book = serializer.save()

如果创建序列化器对象的时候,没有传递instance实例,则调用save()方法的时候,create()被调用,相反,如果传递了instance实例,则调用save()方法的时候,update()被调用。

1
2
3
4
5
6
7
8
9
# 路由
from django.urls import path,re_path ,include

from sers.views import BookView,BookDetailView

urlpatterns = [
path('books/', BookView.as_view()),
re_path('books/(?P<pk>\d+)/', BookDetailView.as_view()),
]
1
2
3
# 序列化器
# 声明序列化器,所有的序列化器都要直接或者间接继承于 Serializer
# 其中,ModelSerializer是Serializer的子类,ModelSerializer在Serializer的基础上进行了代码简化

4.附加说明

1) 在对序列化器进行save()保存时,可以额外传递数据,这些数据可以在create()和update()中的validated_data参数获取到

1
2
## request.user 是django中记录当前登录用户的模型对象
serializer.save(owner=request.user)

2)默认序列化器必须传递所有required的字段,否则会抛出验证异常。但是我们可以使用partial参数来允许部分字段更新

1
2
## Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)

序列化与反序列化简单应用

1
2
3
4
5
/book/ 		GET			查看所有资源,返回所有资源
/book/ POST 添加资源,返回添加资源
/book/1 GET 查看某个资源,返回这一个资源
/book/1 PUT 编辑某个资源,返回编辑之后的这个资源
/book/1 DELETE 查看某个资源,返回空

models.py

1
2
3
4
5
6
7
from django.db import models
# Create your models here.

class Book(models.Model):
title = models.CharField(max_length=32,verbose_name="书籍名称")
price = models.IntegerField(verbose_name="价格", null=True) # 可以为空
pub_date = models.DateField(verbose_name="出版日期")
1
2
3
# 生成数据库
python manage.py makemigrations
python manage.py migrate
1
2
3
4
5
6
# 进入当前虚拟环境 python控制台 新建2条数据
>> from sers.models import Book
>> Book.objects.create(title="西游记",price="199",pub_date="2012-12-12")
<Book: Book object (1)>
>> Book.objects.create(title="水浒传",price="299",pub_date="2011-11-12")
<Book: Book object (2)>

我们想为Book模型类提供一个序列化器,可以定义如下:

serializers.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from rest_framework import serializers
from .models import Book


# 针对模型设计序列化器
class BookSerializers(serializers.Serializer):
title = serializers.CharField(max_length=32)
price = serializers.IntegerField(required=False) # 可以为空
date = serializers.DateField(source="pub_date")

def create(self, validated_data):
# 添加数据
new_book = Book.objects.create(**self.validated_data)
return new_book

def update(self, instance, validated_data):
# 更新数据
Book.objects.filter(pk=instance.pk).update(**validated_data) # 返回是更新的条数
# 获取更新后的数据
updated_book = Book.objects.get(pk=instance.pk)
return updated_book

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

1
2
3
4
5
6
7
8
# 主路由 urls.py
path("sers/", include("sers.urls")),
# sers.urls
from django.urls import path,re_path
from sers import views
path("book/", views.BookView.as_view()),
path("book/<int:id>/", views.BookDetailView.as_view())
#re_path("book/(\d+)", views.BookDetailView.as_view()) 也可以这样写,自己决定
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
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.http import Http404
from .models import Book
from .serializers import BookSerializers


class BookView(APIView): # 针对所有数据
def get(self, request):
# 获取所有书籍
book_list = Book.objects.all() # 返回的是 queryset对象[book01,book02,...]

# 构建序列化器对象
# instance 序列化 data 反序列化 many=True 序列化多条
serializer = BookSerializers(instance=book_list, many=True)
'''
serializer 做了些什么
temp = []
for obj in book_list:
d = {}
d[title] = obj.title
d[price] = obj.price
d[pub_date] = obj.pub_date
temp.append(d)
'''
# serializer.data 构建序列化后的结果 针对的数据是 serializer.instance
return Response(serializer.data)

def post(self, request):
# 获取请求数据
print("data", request.data)
# 构建序列化器对象
serializer = BookSerializers(data=request.data)
# 跟定义BookSerializers序列化器校验规则 返回一个布尔值,所有字段校验通过才返回True
if serializer.is_valid():
# 数据校验通过,数据插入数据库 执行序列化器的create方法
serializer.save()
# 返回序列化后的数据
return Response(serializer.data)
else:
# 校验失败
return Response(serializer.errors)


class BookDetailView(APIView): # 针对单一数据
def _check_book_exists(self, id):
# 先行校验数据是否存在,不存在返回404
if not Book.objects.filter(pk=id).exists():
raise Http404()

def get(self, request, id):
self._check_book_exists(id)
# pk指模型的主键字段
book = Book.objects.get(pk=id) # 获取数据
# book = Book.objects.get(title=title) # 获取数据
serializer = BookSerializers(instance=book, many=False) # 序列化
return Response(serializer.data) # 返回序列化后的数据

def put(self, request, id):
self._check_book_exists(id)
update_book = Book.objects.get(pk=id)
# 反序列化 前端提交的更新数据 request.data
# 序列化 想要更新的数据库对象 update_book
# many默认False 可写可不写
serializer = BookSerializers(instance=update_book, data=request.data, many=False)
# 数据校验
if serializer.is_valid():
# # 不用序列化器 更新数据 这样操作
# Book.objects.filter(pk=id).update(**serializer.validated_data) # 返回是更新的条数
# # 获取更新后的数据
# updated_book = Book.objects.get(pk=id)
# # 序列化
# # serializer = BookSerializers(instance=updated_book)
# # 这个是上面注释的简写,因为上面已经生成serializer对象了
# serializer.instance = updated_book
# # 因为serializer.data 是针对的 serializer.instance 数据做的序列化
# return Response(serializer.data)

serializer.save() # 使用序列化器 update 更新数据
return Response(serializer.data)
else:
return Response(serializer.errors) # 返回报错

def delete(self, request, id):
self._check_book_exists(id)
Book.objects.get(pk=id).delete()
return Response()

BookView 类

  • get 方法:
    • 获取所有书籍的QuerySet对象。
    • 使用BookSerializers序列化器将查询集序列化为JSON格式。
    • 返回序列化后的数据作为HTTP响应。
  • post 方法:
    • 获取客户端提交的POST数据(通常是JSON格式)。
    • 使用BookSerializers序列化器进行反序列化(即验证数据)。
    • 如果数据有效,调用序列化器的save()方法将数据保存到数据库。
    • 返回序列化后的新创建的数据或错误消息。

BookDetailView 类

  • _check_book_exists方法

    • 定义了一个名为 _check_book_exists 的辅助方法,检查该 ID 对应的书籍是否存在,如果不存在,它会抛出一个 Http404 异常,这是 Django 的一个标准异常,用于处理 404 错误
    • 使用 raise Http404() 是合适的,并且 DRF 也会正确处理这个异常
    • 注意,虽然这里使用了异常处理,但这不是装饰器。异常是 Python 中的一种控制流机制,用于处理异常情况,这里使用 Http404 异常来优雅地处理书籍不存在的情况,在视图方法中调用辅助方法来得清晰和直接
    • 如果你有更好的dispatch 重写方法,你也可以重写dispatch 来替代_check_book_exists
  • get 方法:

    • 根据主键获取单个书籍对象。
    • 使用BookSerializers序列化器将书籍对象序列化为JSON格式。
    • 返回序列化后的数据作为HTTP响应。
  • put 方法:

    • 根据主键获取要更新的书籍对象。
    • 使用BookSerializers序列化器进行反序列化(更新数据)。
    • 如果数据有效,调用序列化器的save()方法更新数据库中的数据。
    • 返回序列化后的更新后的数据或错误消息。
    • PUT请求(或类似的更新请求)中,你既需要instance参数,也需要data参数。这是因为:
      • instance参数用于指定你想要更新的数据库对象。这通常是通过查询数据库得到的现有对象。
      • data参数则用于传入客户端发送的更新数据,这通常是一个包含新字段值的字典。
      • 序列化器使用这两个参数来执行以下操作:
        1. 使用instance参数中的对象作为更新的基础。
        2. 使用data参数中的新数据来验证和更新instance对象。
  • delete 方法:

    • 根据主键删除书籍对象。
    • 返回一个空的HTTP响应(虽然在实际应用中,通常返回状态码和消息来确认删除操作)

    put方法中,首先通过Book.objects.get(pk=id)获取到现有的书籍对象(update_book),然后将这个对象作为instance参数传入序列化器。同时,你将客户端发送的更新数据(request.data)作为data参数传入。这样,序列化器就能知道它应该更新哪个对象(instance),以及应该使用哪些新数据来更新(data)。

    最后,通过调用serializer.is_valid(),你可以确保客户端发送的数据是有效的,并且符合你的序列化器定义的字段和验证规则。如果验证通过,你就可以安全地调用serializer.save()来保存更新到数据库了。

知识点梳理:

  1. APIView:
    • APIView是Django REST framework提供的一个基类,用于构建API的视图。它包含了一些用于处理HTTP请求和响应的方法。
  2. Response:
    • Response是Django REST framework中用于构建HTTP响应的类。它接收数据(通常是序列化后的数据)和状态码,然后返回一个HTTP响应。
  3. Model与QuerySet:
    • Book是一个Django模型,代表数据库中的书籍数据。
    • Book.objects.all()返回一个包含所有书籍的QuerySet对象。
    • Book.objects.get(pk=id)根据主键获取单个书籍对象。
  4. Serializer:
    • 序列化器(Serializer)用于将复杂的数据类型(如Django模型或查询集)转换为Python数据类型,以便可以轻松地渲染为JSON、XML或其他内容类型。
    • 在你的代码中,BookSerializers是一个序列化器,用于处理Book模型的数据。
  5. HTTP方法:
    • GET: 用于从服务器请求数据。
    • POST: 用于向服务器提交数据,通常用于创建新记录。
    • PUT: 用于更新服务器上的数据。
    • DELETE: 用于删除服务器上的数据。
  6. 数据校验:
    • 序列化器可以执行数据校验。当调用serializer.is_valid()时,序列化器会检查传入的数据是否符合定义的字段和验证规则。
  7. 序列化与反序列化:
    • 序列化:将复杂的Python对象转换为Python数据类型或JSON格式,以便可以传输或存储。
    • 反序列化:将JSON或其他格式的数据转换为Python对象,以便可以在程序中处理。

GET http://127.0.0.1:8000/sers/book/

1
2
3
4
5
6
7
8
9
10
11
12
[
{
"title": "西游记",
"price": 199,
"date": "2012-12-11"
},
{
"title": "水浒传",
"price": 299,
"date": "2011-11-12"
}
]

post

GET http://127.0.0.1:8000/sers/book/2/

1
2
3
4
5
{
"title": "水浒传",
"price": 299,
"date": "2011-11-12"
}

GET http://127.0.0.1:8000/sers/book/111/

1
2
3
{
"detail": "Not found."
}

PUT http://127.0.0.1:8000/sers/book/2/

PUT

PUT http://127.0.0.1:8000/sers/book/111/

1
2
3
{
"detail": "Not found."
}

DELETE http://127.0.0.1:8000/sers/book/3/ 返回空

DELETE http://127.0.0.1:8000/sers/book/111/

1
2
3
{
"detail": "Not found."
}

案例:前端想要的是date字段不是pub_date字段,但是自己的模型类是pub_date

1
2
3
4
5
class BookSerializers(serializers.Serializer):
title = serializers.CharField(max_length=32)
price = serializers.IntegerField()
# source参数用于指定序列化或反序列化时字段的来源
date = serializers.DateField(source="pub_date")

这里的date = serializers.DateField(source="pub_date")意味着在序列化时,Book模型的pub_date字段的值会被用来填充序列化器中的date字段。同样地,在反序列化时,当你为date字段提供一个值时,这个值会被用来更新Book模型的pub_date字段。

简而言之,source参数允许你指定一个不同的字段名或方法名作为序列化或反序列化的来源。这在处理模型字段和API字段之间命名不一致的情况时非常有用。

1
2
3
4
5
6
7
8
9
10
11
12
[
{
"title": "西游记",
"price": 199,
"date": "2012-12-11"
},
{
"title": "水浒传111",
"price": 119,
"date": "2016-12-11"
}
]

模型类序列化器

如果我们想要使用序列化器对应的是Django的模型类,DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类。

ModelSerializer与常规的Serializer相同,但提供了:

  • 基于模型类自动生成一系列字段
  • 基于模型类自动为Serializer生成validators,比如unique_together
  • 包含默认的create()和update()的实现

1.定义

我们之前的序列化器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from rest_framework import serializers
from .models import Book
class BookSerializers(serializers.Serializer):
title = serializers.CharField(max_length=32)
price = serializers.IntegerField(required=False) # 可以为空
date = serializers.DateField(source="pub_date")

def create(self, validated_data):
# 添加数据
new_book = Book.objects.create(**self.validated_data)
return new_book

def update(self, instance, validated_data):
# 更新数据
Book.objects.filter(pk=instance.pk).update(**validated_data) # 返回是更新的条数
# 获取更新后的数据
updated_book = Book.objects.get(pk=instance.pk)
return updated_book

用模型类后的序列化器

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

不需要再写 update()create() 方法,这得益于 Django REST framework 的强大功能。序列化器内部已经实现了基本的 create()update() 方法,这些方法基于你提供的模型和字段信息自动处理模型的创建和更新

我们可以在python manage.py shell中查看自动生成的BookSerializer的具体实现

注意,只有序列化器改为BookSerializer,其他部分都没动

2.指定字段

  1. 使用fields来明确字段,__all__表名包含所有字段,也可以写明具体哪些字段,如
1
2
3
4
5
class BookSerializers(serializers.ModelSerializer):
class Meta:
model = Book
# fields = '__all__' # 查看所有
fields = ["title","price"] # 查看指定字段
  1. 使用exclude可以明确排除掉哪些字段
1
2
3
4
class BookSerializers(serializers.ModelSerializer):
class Meta:
model = Book
exclude = ["price"] # 查看除了price 其他所有字段

3.重命名字段名

1
2
3
4
5
6
7
8
9
10
class BookSerializers(serializers.ModelSerializer):
date = serializers.DateField(source="pub_date")
class Meta:
model = Book
# fields 不可以跟 exclude 同时用
# __all__ 会出现两个字段date pub_date,不影响任何
# 重命名字段时用 __all__ 时 建议用 exclude 或一个个指明字段
# fields = '__all__'
# fields = ('id', 'title', 'date')
exclude = ["pub_date"]
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
# __all__
[
{
"id": 1,
"date": "2012-12-11",
"title": "西游记",
"price": 199,
"pub_date": "2012-12-11"
},
{
"id": 2,
"date": "2016-12-11",
"title": "水浒传111",
"price": 119,
"pub_date": "2016-12-11"
}
]
# exclude
[
{
"id": 1,
"date": "2012-12-11",
"title": "西游记",
"price": 199
},
{
"id": 2,
"date": "2016-12-11",
"title": "水浒传111",
"price": 119
}
]
  1. 指明只读字段

可以通过read_only_fields指明只读字段,即仅用于序列化输出的字段

1
2
3
4
5
class BookSerializers(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
read_only_fields = ["title",'price']

4.添加一些额外的验证规则

我们可以使用extra_kwargs参数为ModelSerializer添加或修改原有的选项参数

现在,你希望创建一个序列化器来序列化和反序列化这个模型,同时你希望对某些字段添加一些额外的验证规则。你可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ('title', 'price', 'pub_date')
extra_kwargs = {
'title': {'max_length': 32}, # 标题的最大长度
'price': {'required': False, 'min_value': 0}, # 价格字段不是必需的,且必须大于或等于0
'pub_date': {'required': True}, # 出版日期是必需的
}

仅供参考

自定义反序列化字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
## 一些只参与反序列化的字段,但是不是与数据库关联的
## 在序列化类中规定,并在校验字段时从校验的参数字典中剔除
class PublishModelSerializer(serializers.ModelSerializer):
# 自定义不入库的 反序列化 字段
re_name = serializers.CharField(write_only=True)
class Meta:
model = models.Publish
fields = ('name', 're_name', 'address')
def validate(self, attrs):
name = attrs.get('name')
re_name = attrs.pop('re_name') # 剔除
if name != re_name:
raise serializers.ValidationError({'re_name': '确认名字有误'})
return attrs

模型类中自定义序列化深度

1
2
3
4
5
6
7
8
## model类中自定义插拔的外键序列化字段,可以采用外键关联表的序列化类来完成深度查询
class Book(BaseModel):
# ...
@property
def publish_detail(self):
from . import serializers
data = serializers.PublishModelSerializer(self.publish).data
return data

接口操作总结

1
## 单查群查、单删群删、单增群增、单改群改

路由层:api/url.py

1
2
3
4
5
6
7
from django.conf.urls import url
from . import views
urlpatterns = [
# ...
url(r'^v3/books/$', views.BookV3APIView.as_view()),
url(r'^v3/books/(?P<pk>.*)/$', views.BookV3APIView.as_view()),
]

模型层:api/models.py

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
92
## 修改部分:取消book类 name 与 publish 联合唯一,
from django.db import models
from utils.model import BaseModel
class Book(BaseModel):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=5, decimal_places=2)
img = models.ImageField(upload_to='img', default='img/default.png')
publish = models.ForeignKey(to='Publish', null=True,
related_name='books',
db_constraint=False,
on_delete=models.DO_NOTHING,
)
authors = models.ManyToManyField(to='Author', null=True,
related_name='books',
db_constraint=False,
)

@property
def publish_name(self):
return self.publish.name

@property
def authors_info(self):
author_list = []
for author in self.authors.all():
author_list.append({
'name': author.name,
'age': author.age,
'mobile': author.detail.mobile
})
return author_list

@property
def publish_bac(self):
from . import serializers
data = serializers.PublishModelSerializer(self.publish).data
return data

class Meta:
db_table = 'old_boy_book'
verbose_name = '书籍'
verbose_name_plural = verbose_name
# 联合唯一
# unique_together = ('name', 'publish')

def __str__(self):
return self.name

class Publish(BaseModel):
name = models.CharField(max_length=64)
address = models.CharField(max_length=64)

class Meta:
db_table = 'old_boy_publish'
verbose_name = '出版社'
verbose_name_plural = verbose_name

def __str__(self):
return self.name

class Author(BaseModel):
name = models.CharField(max_length=64)
age = models.IntegerField()

@property
def mobile(self):
return self.detail.mobile

class Meta:
db_table = 'old_boy_author'
verbose_name = '作者'
verbose_name_plural = verbose_name

def __str__(self):
return self.name

class AuthorDetail(BaseModel):
mobile = models.CharField(max_length=11)

author = models.OneToOneField(to='Author', null=True,
related_name='detail',
db_constraint=False,
on_delete=models.CASCADE
)

class Meta:
db_table = 'old_boy_author_detail'
verbose_name = '作者详情'
verbose_name_plural = verbose_name

def __str__(self):
return '%s的详情' % self.author.name

序列化层:api/serializers.py

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
## 群增与群改反序列化实现
## 1)ModelSerializer默认不通过群改功能,需要在Meta中设置 list_serializer_class
## 2)自定义ListSerializer子类,重写update方法,将子类绑定给 list_serializer_class
## 3)重写update方法中通过 代表要更新的对象们instance 及 提供的更新数据们validated_data
## 得到 更新后的对象们instance 返回
class BookV3ListSerializer(serializers.ListSerializer):
def update(self, instance, validated_data):
'''
:param instance: [book_obj1, ..., book_obj2]
:param validated_data: [{更新数据的字段}, ..., {更新数据的字段}]
:return: [book_new_obj1, ..., book_new_obj2]
'''
for index, obj in enumerate(instance): # type: int, models.Book
# 单个对象数据更新 - 一个个属性更新值
for attr, value in validated_data[index].items():
# 对象有更新数据字典的key对应的属性,才完成该属性的值更新
if hasattr(obj, attr):
setattr(obj, attr, value)
# 信息同步到数据库
obj.save()
return instance

class BookV3ModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = ('name', 'price', 'publish', 'authors', 'img', 'publish_name', 'authors_info')
list_serializer_class = BookV3ListSerializer
extra_kwargs = {
'publish': {
'required': True,
'write_only': True
},
'authors': {
'required': True,
'write_only': True
},
'img': {
'read_only': True
}
}
def validate_name(self, value):
if 'sb' in value:
raise serializers.ValidationError('书名有敏感词汇')
return value
def validate(self, attrs):
name = attrs.get('name')
publish = attrs.get('publish')
if models.Book.objects.filter(name=name, publish=publish):
raise serializers.ValidationError({'book': '书籍以存在'})
return attrs

视图层:api/views.py

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
92
93
94
95
96
97
98
99
100
101
102
103
class BookV3APIView(APIView):
# 单查群查
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
book_obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
if not book_obj:
return APIResponse(1, 'pk error')
book_ser = serializers.BookV3ModelSerializer(book_obj)
return APIResponse(0, 'ok', results=book_ser.data)
book_query = models.Book.objects.filter(is_delete=False).all().order_by('-id')
book_ser = serializers.BookV3ModelSerializer(book_query, many=True)
return APIResponse(0, 'ok', results=book_ser.data)

# 单增群增
def post(self, request, *args, **kwargs):
# 单增 /books/ data {}
# 群增 /books/ data [{}, ..., {}]
request_data = request.data
if isinstance(request_data, dict):
data = [request_data, ]
elif isinstance(request_data, list):
data = request_data
else:
return APIResponse(1, '数据格式有误')
book_ser = serializers.BookV3ModelSerializer(data=data, many=True)
if book_ser.is_valid():
book_obj_list = book_ser.save()
return APIResponse(0, 'ok',
results=serializers.BookV3ModelSerializer(book_obj_list, many=True).data
)
else:
return APIResponse(1, '添加失败', results=book_ser.errors)

"""
1)先确定要更新的对象 主键们 与 数据们
2)通过校验数据库剔除 已删除的对象 与 不存在的对象
主键们 => 剔除过程 => 可以修改的对象们
数据们 => 剔除过程 => 可以修改的对象们对应的数据们
3)反序列化及校验过程
通过 => save() 完成更新
失败 => ser_obj.errors 返回
"""
# 单改群改
def patch(self, request, *args, **kwargs):
# 单改 /books/(pk)/ data {"name": "new_name", ...}
# 群改 /books/ data [{"pk": 1, "name": "new_name", ...}, ...,{"pk": n, "name": "new_name", ...}]
# 结果:
# pks = [1, ..., n] => book_query => instance
# data = [{"name": "new_name", ...}, ..., {"name": "new_name", ...}] => data

# 数据预处理
pk = kwargs.get('pk')
request_data = request.data
if pk:
if not isinstance(request_data, dict):
return APIResponse(1, '单改数据有误')
pks = [pk, ]
data = [request_data, ]
elif isinstance(request_data, list):
try:
pks = []
for dic in request_data:
pks.append(dic.pop('pk'))
data = request_data
except:
return APIResponse(1, '群改数据有误')
else:
return APIResponse(1, '数据格式有误')

# 将 已删除的书籍 与 不存在的书籍 剔除 (提供修改的数据相对应也剔除)
book_query = []
filter_data = []
for index, pk in enumerate(pks):
book_obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
if book_obj:
book_query.append(book_obj)
filter_data.append(data[index])
# 反序列化完成数据的修改
book_ser = serializers.BookV3ModelSerializer(instance=book_query, data=filter_data, many=True, partial=True)
if book_ser.is_valid():
book_obj_list = book_ser.save()
return APIResponse(1, 'ok',
results=serializers.BookV3ModelSerializer(book_obj_list, many=True).data
)
else:
return APIResponse(1, '更新失败', results=book_ser.errors)

# 单删群删
def delete(self, request, *args, **kwargs):
# 单删 /books/(pk)/
# 群删 /books/ 数据包携带 pks => request.data
pk = kwargs.get('pk')
if pk:
pks = [pk]
else:
pks = request.data.get('pks')
if not pks:
return APIResponse(1, '删除失败')
book_query = models.Book.objects.filter(is_delete=False, pk__in=pks)
if not book_query.update(is_delete=True): # 操作的记录大于0就记录删除成功
return APIResponse(1, '删除失败')
return APIResponse(0, '删除成功')