序列化
序列化用于对用户请求数据进行验证和数据进行序列化,序列化器(serializers)类似于Django forms
模型设计
from django.db import models
# Create your models here.
class User(models.Model):
"""
用户信息
"""
name = models.CharField(max_length=32)
pwd = models.CharField(max_length=32)
level_choices = (
(1, 'common'),
(2, 'vip'),
(3, 'svip')
)
level = models.PositiveSmallIntegerField(choices=level_choices, default=1)
group = models.ForeignKey(to='Group')
roles = models.ManyToManyField(to='Role')
def __str__(self):
return self.name
class UserToken(models.Model):
"""
用户令牌
"""
user = models.OneToOneField(to=User)
token = models.CharField(max_length=64)
def __str__(self):
return self.token
class Role(models.Model):
"""
角色
"""
r_name = models.CharField(max_length=32, verbose_name='角色名')
def __str__(self):
return self.r_name
class Group(models.Model):
"""组"""
g_name = models.CharField(max_length=32, verbose_name='组名称')
def __str__(self):
return self.g_name
序列化
序列化一般可以继承两个类,serializers.ModelSerializer
和 serializers.Serializer
。继承 serializers.Serializer
需要手写字段,继承serializers.ModelSerializer
可以复用数据库字段,并且可以创建和更新数据。
serializers.Serializer
class GroupSerializer(serializers.Serializer):
"""
用户序列化类
"""
# g_name 名称和数据库表Group的字段名称需要一致
g_name = serializers.CharField()
serializers.ModelSerializer
class UserSerializer(serializers.ModelSerializer):
"""
{
"code": 1000,
"data": [
{
"id": 1,
"name": "jack",
"pwd": "123",
"level": 3,
"group": 1,
"roles": [
1,
2
]
},
{
"id": 2,
"name": "lily",
"pwd": "123",
"level": 1,
"group": 2,
"roles": [
2
]
},
{
"id": 3,
"name": "lisa",
"pwd": "123",
"level": 1,
"group": 3,
"roles": [
4
]
}
],
"error": null
}
"""
class Meta:
model = User
# fields 也可以接受一个列表
fields = '__all__'
source
source只能得到单个值,不能处理多对多字段
class UserSerializer(serializers.ModelSerializer):
level = serializers.CharField(source='get_level_display')
# group 名称和数据库表一样会覆盖,不一样(写成g_name)就会显示g_name 和 group 两个
group = serializers.CharField(source='group.g_name')
class Meta:
model = User
# fields 也可以接受一个列表
fields = ['level', 'group', 'roles', 'name', 'pwd','id']
多对多字段
class UserSerializer(serializers.ModelSerializer):
level = serializers.CharField(source='get_level_display')
# group 名称和数据库表一样会覆盖,不一样(写成g_name)就会显示g_name 和 group 两个
group = serializers.CharField(source='group.g_name')
roles = serializers.SerializerMethodField()
class Meta:
model = User
# fields 也可以接受一个列表
fields = ['level', 'group', 'roles', 'name', 'pwd','id']
def get_roles(self, obj):
return [i.r_name for i in obj.roles.all()]
class UserSerializer(serializers.ModelSerializer):
level_name = serializers.CharField(source='get_level_display', read_only=True)
# group 名称和数据库表一样会覆盖,不一样(写成g_name)就会显示g_name 和 group 两个
group_name = serializers.CharField(source='group.g_name', read_only=True)
roles = serializers.SerializerMethodField()
class Meta:
model = User
# fields 也可以接受一个列表
fields = ['level', 'group', 'roles', 'name', 'pwd','id']
# depth = 1 # 所有有关系的字段都变成 read_only
# exclude = [] # 排除某个字段
# 每个字段的一些额外参数, 下面表示上面定义的fields中的level和group通过get请求的时候不显示,但是post请求创建数据的时候还是可以提供的
extra_kwargs = {
'group': {'write_only': True},
'level': {'write_only': True},
}
def get_roles(self, obj):
return [i.r_name for i in obj.roles.all()]
超链接API:Hyperlinked
class UserSerializer(serializers.ModelSerializer):
group = serializers.HyperlinkedIdentityField(
view_name='sg',
lookup_field='group_id',
lookup_url_kwarg='pk'
)
class Meta:
model = User
# depth = 1
# fields 也可以接受一个列表
fields = ['level', 'group', 'roles', 'name', 'pwd','id']
对应的urls.py的url: url(r'group/(?P<pk>d+)$', SingleGroupView.as_view(), name='sg')
views.py
class UserView(APIView):
"""用户视图"""
def get(self, request, *args, **kwargs):
ret = {'code': 1000, 'data':None, 'error': None}
users = User.objects.all()
try:
users = User.objects.all()
# 单个对象many=False
gs = UserSerializer(users, many=True)
ret['data'] = gs.data
except Exception as e:
ret['code'] = 1001
ret['error'] = '发生错误'
return Response(ret)
def post(self, request, *args, **kwargs):
ret = {'code': 1000, 'data': None, 'error': None}
# request.data 是一个纯正的字典
gs = UserSerializer(data=request.data)
if gs.is_valid():
# gs.validated_data 是rderedDict,因为roles字段重写成SerializerMethodField,
# 所以默认的validated_data是获取不到值的,需要手动赋值
gs.validated_data['roles'] = request.data['roles']
# 会自动调用create , update 具体是什么方法根据传的参数来定
gs.save()
ret['data'] = request.data
else:
ret['error'] = gs.errors
return Response(ret)
def put(self, request, pk):
user_obj = User.objects.filter(pk=pk).first()
bs = UserSerializer(user_obj, data=request.data, context={'request': request})
if bs.is_valid():
bs.save()
return Response(bs.data)
else:
return HttpResponse(bs.errors)
其中gs.data是列表套有序字典的形式,通过Response会做简单的转换成列表套普通字段的形式
重写create和update
class UserSerializer(ModelSerializer):
class Meta:
model = User
fields = '__all__'
def create(self, validated_data):
roles_list = validated_data.pop('roles')
group = validated_data['group']['g_name']
level = validated_data.pop('get_level_display')
validated_data.pop('group')
obj = User.objects.create(group_id=group, level=level, **validated_data)
obj.roles.add(*roles_list)
return obj
def update(self, instance, validated_data):
group = validated_data['group']['g_name']
validated_data.pop('group')
validated_data['group_id'] = group
if 'get_level_display' in validated_data:
validated_data['level'] = validated_data.pop('get_level_display')
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
return instance
自定义验证字段
class XXValidator(object):
def __init__(self, base):
self.base = base
def __call__(self, value):
if not value.startswith(self.base):
message = '标题必须以 %s 为开头。' % self.base
raise serializers.ValidationError(message)
def set_context(self, serializer_field):
"""
This hook is called by the serializer instance,
prior to the validation call being made.
"""
# 执行验证之前调用,serializer_fields是当前字段对象
pass
class UserGroupSerializer(serializers.Serializer):
# title必须以jack开头,否则调用is_valid()就无法通过
title = serializers.CharField(error_messages={'required':'标题不能为空'},validators=[XXValidator('jack'),])
源码流程
实例化序列类对象的时候,先执行__new__,再执行__init__,根据many的不同分别调用Serializer类处理和ListSerializer类处理。ser.data会调用这两个对象的to_representation方法