定义Serializer
1. 定义方法
Django REST framework中的Serializer使用类来定义,须继承自rest_framework.serializers.Serializer。
例如,我们已有了一个数据库模型类BookInfo
class BookInfo(models.Model):
btitle = models.CharField(max_length=20, verbose_name='名称')
bpub_date = models.DateField(verbose_name='发布日期', null=True)
bread = models.IntegerField(default=0, verbose_name='阅读量')
bcomment = models.IntegerField(default=0, verbose_name='评论量')
image = models.ImageField(upload_to='booktest', verbose_name='图片', null=True)
我们想为这个模型类提供一个序列化器,可以定义如下:
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
btitle = serializers.CharField(label='名称', max_length=20)
bpub_date = serializers.DateField(label='发布日期', required=False)
bread = serializers.IntegerField(label='阅读量', required=False)
bcomment = serializers.IntegerField(label='评论量', required=False)
image = serializers.ImageField(label='图片', required=False)
注意:serializer不是只能为数据库模型类定义,也可以为非数据库模型类的数据定义。serializer是独立于数据库之外的存在。
2. 字段与选项
常用字段类型:
![image image](https://img2018.cnblogs.com/blog/1532087/201905/1532087-20190528142611347-199198847.png)
read_only:True表示不允许用户自己上传,只能用于api的输出。如果某个字段设置了read_only=True,那么就不需要进行数据验证,只会在返回时,将这个字段序列化后返回
write_only:与read_only对应;就是用户post过来的数据,后台服务器处理后不会再经过序列化后返回给客户端;最常见的就是我们在使用手机注册的验证码和填写的密码。
3. 创建Serializer对象
Serializer的构造方法为:
Serializer(instance=None, data=empty, **kwarg)
说明:
1)用于序列化时,将模型类对象传入instance参数
2)用于反序列化时,将要被反序列化的数据传入data参数
3)除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如
serializer = AccountSerializer(account, context={'request': request})
通过context参数附加的数据,可以通过Serializer对象的context属性获取。
Serializer使用
我们定义一个测试视图来使用序列化器,如下:
views
def test(request): return HttpResponse('ok')
urls
urlpatterns = [ url(r'^test/$', views.test) ]
1. 基本使用
def test(request): book = BookInfo.objects.get(id=5) # 先查询出一个图书对象 serializer = BookInfoSerializer(book) # 构建序列化 print(serializer.data) # 获取序列化数据 {'image': None, 'btitle': '西游记', 'id': 5, 'bread': 10, 'bcomment': 10, 'bpub_date': '1988-01-01'} return HttpResponse('ok')
如果要被序列化的是包含多条数据的查询集QuerySet,可以通过添加many=True参数补充说明
def test(request): book = BookInfo.objects.all() # 先查询出一个图书对象 serializer = BookInfoSerializer(book, many=True) # 构建序列化 print(serializer.data) # 获取序列化数据 {'image': None, 'btitle': '西游记', 'id': 5, 'bread': 10, 'bcomment': 10, 'bpub_date': '1988-01-01'} return HttpResponse('ok')
2. 关联对象嵌套序列化
如果需要序列化的数据中包含有其他关联对象,则对关联对象数据的序列化需要指明。
例如,在定义英雄数据的序列化器时,外键hbook(即所属的图书)字段如何序列化?
我们先定义HeroInfoSerialzier除外键字段外的其他部分
对于关联字段,可以采用以下几种方式:
1. PrimaryKeyRelatedField
此字段将被序列化为关联对象的主键。
hbook = serializers.PrimaryKeyRelatedField(label='图书', read_only=True) 或 hbook = serializers.PrimaryKeyRelatedField(label='图书', queryset=BookInfo.objects.all())
指明字段时需要包含read_only=True或者queryset参数:
- 包含read_only=True参数时,该字段将不能用作反序列化使用
- 包含queryset参数时,将被用作反序列化时参数校验使用
使用效果:
def test(request): hero = HeroInfo.objects.get(id=10) # 查询一个英雄对象 serializer = HeroInfoSerializer(hero) # 构建序列化 print(serializer.data) # 返回序列化内容 {'id': 10, 'hcomment': '独孤九剑', 'hname': '令狐冲', 'hbook': 3, 'hgender': 1} return HttpResponse('ok')
报错:说明id不存在
2. StringRelatedField
此字段将被序列化为关联对象的字符串表示方式(即__str__方法的返回值)
hbook = serializers.StringRelatedField(label='图书')
def test(request): hero = HeroInfo.objects.get(id=11) # 查询一个英雄对象 serializer = HeroInfoSerializer(hero) # 构建序列化 print(serializer.data) # 返回序列化内容 {'hgender': 0, 'hbook': '笑傲江湖', 'id': 11, 'hname': '任盈盈', 'hcomment': '弹琴'} return HttpResponse('ok')
3. 使用关联对象的序列化器
hbook = BookInfoSerializer()
def test(request): hero = HeroInfo.objects.get(id=11) # 查询一个英雄对象 serializer = HeroInfoSerializer(hero) # 构建序列化 print(serializer.data) # 返回序列化内容 {'hgender': 0, 'hbook': OrderedDict([('id', 3), ('btitle', '笑傲江湖'), ('bpub_date', '1995-12-24'), ('bread', 20), ('bcomment', 80), ('image', None)]), 'hcomment': '弹琴', 'id': 11, 'hname': '任盈盈'} return HttpResponse('ok')
4. HyperlinkedRelatedField
此字段将被序列化为获取关联对象数据的接口链接
hbook = serializers.HyperlinkedRelatedField(label='图书', read_only=True, view_name='books-detail')
必须指明view_name参数,以便DRF根据视图名称寻找路由,进而拼接成完整URL。
{'id': 6, 'hname': '乔峰', 'hgender': 1, 'hcomment': '降龙十八掌', 'hbook': 'http://127.0.0.1:8000/books/2/'}
5. SlugRelatedField
此字段将被序列化为关联对象的指定字段数据
slug_field指明使用关联对象的哪个字段
hbook = serializers.SlugRelatedField(label='图书', read_only=True, slug_field='bpub_date')
def test(request): hero = HeroInfo.objects.get(id=11) # 查询一个英雄对象 serializer = HeroInfoSerializer(hero) # 构建序列化 print(serializer.data) # 返回序列化内容 {'id': 11, 'hgender': 0, 'hcomment': '弹琴', 'hname': '任盈盈', 'hbook': datetime.date(1995, 12, 24)} return HttpResponse('ok')
6. 重写to_representation方法
序列化器的每个字段实际都是由该字段类型的to_representation方法决定格式的,可以通过重写该方法来决定格式。
注意,to_representations方法不仅局限在控制关联对象格式上,适用于各个序列化器字段类型。
自定义一个新的关联字段:
class BookRelateField(serializers.RelatedField): """自定义用于处理图书的字段""" def to_representation(self, value): return 'Book: %d %s' % (value.id, value.btitle)
hbook = BookRelateField(read_only=True)
def test(request): hero = HeroInfo.objects.get(id=11) # 查询一个英雄对象 serializer = HeroInfoSerializer(hero) # 构建序列化 print(serializer.data) # 返回序列化内容 {'hbook': 'Book: 3 笑傲江湖', 'id': 11, 'hcomment': '弹琴', 'hname': '任盈盈', 'hgender': 0} return HttpResponse('ok')
many参数
如果关联的对象数据不是只有一个,而是包含多个数据,如想序列化图书BookInfo数据,每个BookInfo对象关联的英雄HeroInfo对象可能有多个,此时关联字段类型的指明仍可使用上述几种方式,只是在声明关联字段时,多补充一个many=True参数即可。
此处仅拿PrimaryKeyRelatedField类型来举例,其他相同。
在BookInfoSerializer中添加关联字段:
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
btitle = serializers.CharField(label='名称', max_length=20)
bpub_date = serializers.DateField(label='发布日期', required=False)
bread = serializers.IntegerField(label='阅读量', required=False)
bcomment = serializers.IntegerField(label='评论量', required=False)
image = serializers.ImageField(label='图片', required=False)
heroinfo_set = serializers.PrimaryKeyRelatedField(read_only=True, many=True) # 新增
使用效果:
def test(request): book = BookInfo.objects.get(id=3) # 查询一个英雄对象 serializer = BookInfoSerializer(book) # 构建序列化 print(serializer.data) # 返回序列化内容 {'image': None, 'id': 3, 'bpub_date': '1995-12-24', 'bcomment': 80, 'bread': 20, 'heroinfo_set': [10, 11, 12, 13], 'btitle': '笑傲江湖'} return HttpResponse('ok')