django-Serializer序列化器的简单使用

使用serializer之前要先进行环境的安装和配置

  • 首先安装DRF
pip install djangorestframework

# DRF是以Django扩展应用的方式提供的,所以我们可以直接利用已有的Django环境而无需从新创建。
#(若没有Django环境,需要先创建环境安装Django)
# DRF需要以下依赖:
# Python (2.7, 3.2, 3.3, 3.4, 3.5, 3.6)
# Django (1.10, 1.11, 2.0)
  • 在django项目中注册rest_framework应用
    在settings.py的INSTALLED_APPS中添加'rest_framework'
INSTALLED_APPS = [
    ...
    'rest_framework',
]

在django项目中使用serializer序列化器

我们在django项目中新建一个子应用(bookapp)进行serializer序列化器的使用介绍(自己完成子应用的注册和路由配置):

我们在models.py中定义一个书籍模型类BookInfo和英雄模型类HeroInfo:

# 数据导入见附录
class BookInfo(models.Model):
    btitle = models.CharField(max_length=20,verbose_name="书籍名称")
    bpub_date = models.DateField(null=True,verbose_name="发布日期")
    bread = models.IntegerField(default=0,verbose_name="阅读量")
    bcomment = models.IntegerField(default=0,verbose_name="评论量")
    is_delete = models.BooleanField(default=False,verbose_name="逻辑删除")

    class Meta:
        db_table = "tb_books"  # 表在数据库的名字
        verbose_name = "图书"  #在admin站点显示名称
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.btitle


class HeroInfo(models.Model):

    GENDER_CHOICES = (
        (0, 'female'),
        (1, 'male')
    )
    hname = models.CharField(max_length=20, verbose_name='名称')
    hgender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')
    hcomment = models.CharField(max_length=200, null=True, verbose_name='描述信息')
    hbook = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书')  # 外键
    is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')

    class Meta:
        db_table = 'tb_heros'
        verbose_name = '英雄'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.hname

我们为这两个模型类提供序列化器,可以定义如下:
Django REST framework中的Serializer使用类来定义,须继承自rest_framework.serializers.Serializer。

from rest_framework import serializers

class BookInfoSerializer(serializers.Serializer):
    btitle = serializers.CharField(min_length=3,max_length=20)
    bpub_date = serializers.DateField()
    bread = serializers.IntegerField(read_only=True)
    bcomment = serializers.IntegerField(write_only=True)


class HeroInfoSerializer(serializers.Serializer):
    hname = serializers.CharField()
    hcomment = serializers.CharField()

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

字段与选项的说明

常用字段类型

字段 字段构造方式
BooleanField BooleanField()
NullBooleanField NullBooleanField()
CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugField SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
URLField URLField(max_length=200, min_length=None, allow_blank=False)
UUIDField UUIDField(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"
IPAddressField IPAddressField(protocol='both', unpack_ipv4=False, **options)
IntegerField IntegerField(max_value=None, min_value=None)
FloatField FloatField(max_value=None, min_value=None)
DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None)
max_digits: 最多位数
decimal_palces: 小数点位置
DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationField DurationField()
ChoiceField ChoiceField(choices)
choices与Django的用法相同
MultipleChoiceField MultipleChoiceField(choices)
FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListField ListField(child=, min_length=None, max_length=None)
DictField DictField(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页面时,显示的字段帮助提示信息

创建Serializer对象格式说明

Serializer的构造方法为:

serializer = BookInfoSerializer(instance=None, data=empty, **kwarg)

说明:
1)用于序列化时,将模型类对象传入instance参数
2)用于反序列化时,将要被反序列化的数据传入data参数
3)除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如

serializer = BookInfoSerializer(instance=None, data=empty, context={'request': request})
# 通过context参数附加的数据,可以通过Serializer对象的context属性获取。

序列化使用

我们在django shell中来学习序列化器的使用。

python manage.py shell
1 基本使用

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

from bookapp.models import BookInfo

book = BookInfo.objects.get(id=2)

2) 构造序列化器对象

from bookapp.serializers import BookInfoSerializer

serializer = BookInfoSerializer(book)

3)获取序列化数据

# 通过data属性可以获取序列化后的数据,序列化器定义了几个字段就序列化几个字段
serializer.data
# { 'btitle': '天龙八部', 'bpub_date': '1986-07-24', 'bread': 36, 'bcomment': 40}
# 实际开发中直接将序列化后的数据返回给前端开发人员使用

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

books = BookInfo.objects.all()
serializer = BookInfoSerializer(books, many=True)
serializer.data
# [OrderedDict([('btitle', '天龙八部'), ('bpub_date', '1986-07-24'), ('bread', 36), ('bcomment', 40)], OrderedDict([('btitle', '笑傲江湖'), ('bpub_date', '1995-12-24'), ('bread', 20), ('bcomment', 80)]), OrderedDict([('btitle', '雪山飞狐'), ('bpub_date', '1987-11-11'), ('bread', 58), ('bcomment', 24)]), OrderedDict([('btitle', '西游记'), ('bpub_date', '1988-01-01'), ('bread', 10), ('bcomment', 10)])]
2 关联对象嵌套序列化

对序列化器添加代码:

from rest_framework import serializers

class BookInfoSerializer1(serializers.Serializer):
    btitle = serializers.CharField()
    bpub_date = serializers.DateField()

class HeroInfoSerializer(serializers.Serializer):
    hname = serializers.CharField()
    hcomment = serializers.CharField()
    # 此字段将被序列化为关联对象的主键。
    hbook = serializers.PrimaryKeyRelatedField(read_only=True)
    #此字段将被序列化为关联对象的字符串表示方式(即__str__方法的返回值)
    hbook = serializers.StringRelatedField(read_only=True)
    # 使用关联对象的序列化器
    hbook = BookInfoSerializer1()


class BookInfoSerializer(serializers.Serializer):
    btitle = serializers.CharField(min_length=3,max_length=20)
    bpub_date = serializers.DateField()
    bread = serializers.IntegerField(read_only=True)
    # 此字段将被序列化为关联对象的主键。
    heroinfo_set = serializers.PrimaryKeyRelatedField(many=True,read_only=True)
    # 此字段将被序列化为关联对象的字符串表示方式(即__str__方法的返回值)
    heroinfo_set = serializers.StringRelatedField(read_only=True,many=True)
    # 使用关联对象的序列化器
    heroinfo_set = HeroInfoSerializer(many=True)

以BookInfoSerializer序列化器的代码进行说明:
1) PrimaryKeyRelatedField:
指明字段时需要包含read_only=True或者queryset参数

# 此字段将被序列化为关联对象的主键。
heroinfo_set = serializers.PrimaryKeyRelatedField(many=True,read_only=True)

shell中使用效果:

from bookapp.serializers import BookInfoSerializer
from bookapp.models import BookInfo

book = BookInfo.objects.get(id=2)
serializer = BookInfoSerializer(book)
serializer.data
{'bread': 36, 'heroinfo_set': [6, 7, 8, 9], 'bpub_date': '1986-07-24', 'btitle': '天龙八部'}

2) StringRelatedField

    # 此字段将被序列化为关联对象的字符串表示方式(即__str__方法的返回值)
    heroinfo_set = serializers.StringRelatedField(read_only=True,many=True)

shell中使用效果:

from bookapp.serializers import BookInfoSerializer
from bookapp.models import BookInfo

book = BookInfo.objects.get(id=2)
serializer = BookInfoSerializer(book)
serializer.data
{'heroinfo_set': ['乔峰', '段誉', '虚竹', '王语嫣'], 'btitle': '天龙八部', 'bread': 36, 'bpub_date': '1986-07-24'}

3)使用关联对象的序列化器

    # 使用关联对象的序列化器
    heroinfo_set = HeroInfoSerializer(many=True)

shell中使用效果:

from bookapp.serializers import BookInfoSerializer
from bookapp.models import BookInfo

book = BookInfo.objects.get(id=2)
serializer = BookInfoSerializer(book)
serializer.data
{'btitle': '天龙八部', 'bread': 36, 'bpub_date': '1986-07-24', 'heroinfo_set': [OrderedDict([('hname', '乔峰'), ('hcomment', '降龙十八掌')]), OrderedDict([('hname', '段誉'), ('hcomment', '六脉神剑')]), OrderedDict([('hname', '虚竹'), ('hcomment', '天山[('hname', '王语嫣'), ('hcomment', '神仙姐姐')])]}

many参数
如果关联的对象数据不是只有一个,而是包含多个数据,如想序列化图书BookInfo数据,每个BookInfo对象关联的英雄HeroInfo对象可能有多个,此时关联字段类型的指明仍可使用上述几种方式,只是在声明关联字段时,多补充一个many=True参数即可。

反序列化使用

1 验证

使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。
在获取反序列化的数据前,必须调用is_valid()方法进行验证,验证成功返回True,否则返回False。
验证失败,可以通过序列化器对象的errors属性获取错误信息,返回字典,包含了字段和字段的错误。如果是非字段错误,可以通过修改REST framework配置中的NON_FIELD_ERRORS_KEY来控制错误字典中的键名。
验证成功,可以通过序列化器对象的validated_data属性获取数据。
在定义序列化器时,指明每个字段的序列化类型和选项参数,本身就是一种验证行为。
如我们前面定义过的BookInfoSerializer:

btitle = serializers.CharField(min_length=3,max_length=20)

在反序列化时会对btitle的值进行验证其长度是否大于3小于20
我们现在重写BookInfoSerializer序列化器:

from rest_framework import serializers
from book.models import BookInfo

class BookInfoSerializer(serializers.Serializer):
    btitle = serializers.CharField(min_length=3,max_length=20)
    bpub_date = serializers.DateField()
    bread = serializers.IntegerField(read_only=True)
    # 序列化附表的主建
    # heroinfo_set = serializers.PrimaryKeyRelatedField(many=True,read_only=True)
    # 序列化附表的__str__方法的返回值
    # heroinfo_set = serializers.StringRelatedField(read_only=True,many=True)
    # 使用附表的序列化器
    # heroinfo_set = HeroInfoSerializer(many=True)

    # 定义单一字段验证的方法
    def validate_btitle(self, value):
        if value == 'python':
            raise serializers.ValidationError('书名不符合规范')
        return value

    # 定义多字段验证方法
    def validate(self, attrs):
        if attrs['btitle'] == 'itcast':
            raise serializers.ValidationError('itcast不符合规范')
        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.btitle = validated_data['btitle']
        instance.bpub_date = validated_data['bpub_date']
        instance.save()
        return instance

我们在bookapp的views中书写使用的代码:

class BooksView(View):
    # 保存图书
    def post(self, request):
        data_dict = json.loads(request.body.decode())

        # 构造序列化对象,并将要反序列化的数据传递给data构造参数,进而进行验证
        serializer = BookInfoSerializer(data=data_dict)
        # 验证数据,数据错误时直接返回给前端错误提示
        serializer.is_valid(raise_exception=True)
        # 保存数据到数据库
        serializer.save()

        return http.JsonResponse(serializer.data)

class BookView(View):
    # 更新单一图书
    def put(self, request, pk):

        data_dict = json.loads(request.body.decode())
        book = BookInfo.objects.get(id = pk)
        # 创建序列化对象,并将要反序列化的数据传递给data构造参数,进而进行验证
        serializer = BookInfoSerializer(book,data=data_dict)

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

        # 3、更新数据,在反序列化数据的时候,就可以通过save()方法返回一个数据对象实例了
        # 如果创建序列化器对象的时候,没有传递instance实例,则调用save()方法的时候,create()被调用,相反,如果传递了instance实例,则调用save()方法的时候,update()被调用。
        serializer.save()
        return http.JsonResponse(serializer.data)

注意:代码中的注释有对方法的详细说明

附录

模型类数据导入sql:

insert into tb_books(btitle,bpub_date,bread,bcomment,is_delete) values
('射雕英雄传','1980-5-1',12,34,0),
('天龙八部','1986-7-24',36,40,0),
('笑傲江湖','1995-12-24',20,80,0),
('雪山飞狐','1987-11-11',58,24,0);


insert into tb_heros(hname,hgender,hbook_id,hcomment,is_delete) values
('郭靖',1,1,'降龙十八掌',0),
('黄蓉',0,1,'打狗棍法',0),
('黄药师',1,1,'弹指神通',0),
('欧阳锋',1,1,'蛤蟆功',0),
('梅超风',0,1,'九阴白骨爪',0),
('乔峰',1,2,'降龙十八掌',0),
('段誉',1,2,'六脉神剑',0),
('虚竹',1,2,'天山六阳掌',0),
('王语嫣',0,2,'神仙姐姐',0),
('令狐冲',1,3,'独孤九剑',0),
('任盈盈',0,3,'弹琴',0),
('岳不群',1,3,'华山剑法',0),
('东方不败',0,3,'葵花宝典',0),
('胡斐',1,4,'胡家刀法',0),
('苗若兰',0,4,'黄衣',0),
('程灵素',0,4,'医术',0),
('袁紫衣',0,4,'六合拳',0);
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,408评论 4 371
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,690评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,036评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,726评论 0 221
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,123评论 3 296
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,037评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,178评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,964评论 0 213
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,703评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,863评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,333评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,658评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,374评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,195评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,988评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,167评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,970评论 2 279

推荐阅读更多精彩内容