rest_framework的序列化
- 使用restframework的序列化器的时候注意,传入是一个queryset类型的,需要额外设置 many=True
- 修改数据时,partial=True的含义是 可以对部分进行修改,但是必须也使用 字典包裹。而且外键和关系管理对象 只能使用id更改
1.基本展示
#视图函数中
from app01 import models
from rest_framework.views import APIView
from rest_framework.response import Response
from app01.serializer import BookSerializer
#完成获取全部数据和新增数据
class BookList(APIView):
def get(self, request, *args, **kwargs):
all_books = models.Book.objects.all()
ser_obj = BookSerializer(all_books,many=True)
return Response(ser_obj.data)
def post(self, request, *args, **kwargs):
ser_obj = BookSerializer(data=request.data) #data封装着数据 而不是 POST
if ser_obj.is_valid():
ser_obj.save()
return Response(ser_obj.data)
return Response(ser_obj.errors)
#完成获取单个数据和修改,删除单个数据
class Book(APIView):
def get(self, request, *args, **kwargs):
book_obj = models.Book.object.filter(pk=pk).first()
ser_obj = BookSerializer(book_obj)
return Response(ser_obj.data)
def put(self, request, pk, *args, **kwargs):
book_obj = models.Book.objects.filter(pk=pk).first()
ser_obj = BookSerializer(book_obj,data=request.data, partial=True)
if ser_obj.is_valid():
ser_obj.save()
return Response(ser_obj.data)
return Response(ser_obj.errors)
def delete(self, request, pk, *args, **kwargs):
book_obj = models.Book.objects.filter(pk=pk).first()
if book_obj:
book_obj.delete()
return Response({'msg':'删除成功'})
return Response({'error':'删除错误'})
#序列化器的展示
from rest_framework import serializers
from app import models
class BookSerializer(serializers.Serializer):
title = serializers.Charfield(max_length=32)
price = serializers.DecimalField(max_digits=6, decimal_place=2)
2.有外键的序列化器
- 获取和提交需要使用两个不同的字段,原因是获取和提交的方式不同。同时获取的设置属性 read_only=True 提交的设置属性 write_only=True
- 获取的时候由于serializer没有外键这个方法,需要另构类实现
#方式一 这种方法 外键的名字必须和model相同
class BookSerializer(serializers.Serializer):
pub = PublisherSerializer(read_only=True) #必须是pub
post_pub = serializers.IntergerField(write_only=True)
class PublisherSerializer(serializers.Serializer):
name = serializers.CharField(max_length=32)
#方式二 使用SerializeraMethodField
class BookSerializer(serializers.Serializer):
pub_d1213 = serializer.SerializerMethodField(read_only=True) #名字随意
def get_pub_d1213(self, obj):
ser_obj = PublisherSerializer(obj.pub)
return ser_obj.data
class PublisherSerializer(serializers.Serializer):
name = serializers.CharField(max_length=32)
3.关系管理对象的序列化器
- 获取的时候通过SerializerMethodField 调用自己类下的get_这个字段的方法。
- 获取和提交也和外键一样,需要使用两个字段。
class BookSerializer(serializer.Serializer):
author = serializers.SerializeraMethodField(read_only=True)
def get_author(self, obj):
ser_obj = AuthorSerializer(obj.author.all(), many=True)
return ser_obj.data
class AuthorSerializer(serializer.Serializer):
name = serializer.CharField(max_length=32)
4.序列器里新增和修改
- create
def create(self, validated_data):
obj = models.Book.objects.create(
title=validated_data['title'], price=validated_data['price'], pub_date=validated_data['pub_date'], pub_id=validated_data['post_pub'])
obj.author.set(validated_data['post_author'])
return obj
- update
def update(self, instance, validated_data):
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.pub_id = validated_data.get('post_pub', instance.pub_id)
instance.save()
instance.author.set(validated_data.get('post_author', instance.author.all()))
return instance
5.序列化器里的source
- 关键字: source 作用是可以获得 一些无法直接通过字段获取的值,比如外键 choice字段 关系管理对象等
from rest_framework import serializers
class UserInfoSerializer(serializers.Serializer):
xxxx = serializers.CharField(source='user_type')
ooo = serializers.CharField(source='get_user_type_display') # 相似于models中的get_字段_display,遇到choice就这么使用
gp = serializers.CharField(source='group.title') # 通过点来获取值
rls = serializers.CharField(source='roles.all') # 多对多时,反向点all,返回的时Queryset列表,里面都都是对象
#1、通过source配置相应得字段,可以解决一对多(正向,一对一)和choices字段去值得问题,但是涉及到多对多时,就不能做到更详细得处理了,只能返回对象 <-----
#2、.all方法,只适用于关系管理对象字段上
6.resframework和model的使用
- 优点:减少代码量,同时可以完成所有的增删改查,这里需要注意的是,由这种方法产生的外键和关系管理对象字段都是以id的形式最终表示,所以如果想要以关联的其他字段进行表示,需要重新写相关的字段,并标注对应的
write_only 和 read_only属性。 - 基本逻辑都是,读的时候有外键和关系管理对象的无法直接通过字段读,所以需要构建,写的时候可以直接写。
- 基本格式
class BookSerializer(serializer.Serializer):
pub_info = serializzer.SerializerMethodField(read_only=True) #重写字段
author_info = serializer.SerializerMethodField(read_only=True)
def get_pub_info(self, obj):
ser_obj = PublisherSerializer(obj.pub)
return ser_obj.data
def get_author_info(self.obj):
ser_obj = AuthorSerializer(obj.author.all(), many=True)
return ser_obj.data
class Meta:
model = models.Book
fields = '__all__'
depth = 1 # 没写这之前,我们得到的数据都是如group,roles这种涉及到一对多和多对多的字段都是取到的pk,这之后就可以获得多个每个关联对象的所有字段 1~10范围
extra_kwargs = { #相当于form的widget 批量添加属性
'pub': {'write_only': True},
'author': {'write_only': True}
}
7.部分总结
1、写类
1.1、继承Serializer
class RoleSerializer(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField() # title必须和models中的字段进行对应
1.2、继承ModelSerializer
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = ['id','username'] # '__all__'
2、自定义字段
2.1、通过source:
a. choices字段:xxxx = serializers.CharField(source='user_type')
b. 一对多:gp = serializers.CharField(source='group.title')
2.2、通过SerializerMethodField()和自定义函数
...
def get_rls(self,row): # 自定义函数,get_字段
role_obj_list = row.roles.all()
ret =[]
for item in role_obj_list:
ret.append({'id':item.id,'title':item.title})
return ret
2.3、自定义类
8.继续学习ModelSerializer:深度控制
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = '__all__'
depth = 1 # 没写这之前,我们得到的数据都是如group,roles这种涉及到一对多和多对多的字段都是取到的pk,这之后就可以获得多个每个关联对象的所有字段
class UserinfoView(APIView):
def get(self, *args, **kwargs):
users = models.UserInfo.objects.all()
ser = UserInfoSerializer(instance=users, many=True)
print(ser.data)
ret = json.dumps(ser.data, ensure_ascii=False)
return HttpResponse(ret)
# 所有结构
[
{
"id": 1,
"user_type": 1,
"username": "序号话",
"password": "123",
"group": {
"id": 1,
"title": "A组"
},
"roles": [
{
"id": 1,
"title": "医生"
},
{
"id": 2,
"title": "学生"
}
]
},
{
"id": 2,
"user_type": 2,
"username": "民人接",
"password": "123",
"group": {
"id": 2,
"title": "B组"
},
"roles": [
{
"id": 3,
"title": "元昊"
}
]
}
]
-
小提示
1、depth 表示该表中如果都关系管理对象,就打开该对象的所有字段,表示深度,还可以depth=2 2、官方建议depth=[0~10]
9.序列化时外键生成hyperlink
-
通过
HyperlinkedIdentityField重写外键管理对象字段,生成动态url(必须关联一个对象)class UserInfoSerializer(serializers.ModelSerializer): group = serializers.HyperlinkedIdentityField(view_name='gp',lookup_field='group.id',lookup_url_kwarg='xxx') # 重写group,通过url别名生成路径,必须写别名,我们的url中的pk,系统默认也是取group中的pk,如果换个名字就报错了,可以通过lookup_field重写 class Meta: model = models.UserInfo # fields = '__all__' fields = ['id', 'username', 'password', 'group', 'roles'] depth = 0 # 默认为0 class UserinfoView(APIView): def get(self,request, *args, **kwargs): users = models.UserInfo.objects.all() ser = UserInfoSerializer(instance=users, many=True,context={'request':request}) # 需要写context print(ser.data) ret = json.dumps(ser.data, ensure_ascii=False) return HttpResponse(ret) class GourpSerializer(serializers.ModelSerializer): class Meta: model = models.UserGroup fields = '__all__' class GourpView(APIView): # 对每个组进行详细生成url def get(self,request, *args, **kwargs): pk = kwargs.get('pk') # 通过路径传入 obj = models.UserGroup.objects.filter(pk=pk).first() ser = GourpSerializer(instance=obj, many=False) ret = json.dumps(ser.data, ensure_ascii=False) return HttpResponse(ret) # url.py文件中 urlpatterns = [ ... url(r'(?P<version>[v1|v2]+)/userinfo/$',views.UserinfoView.as_view()), url(r'(?P<version>[v1|v2]+)/group/(?P<pk>\d+)/$',views.GourpView.as_view(),name = 'gp'), ] #[{"id": 1, "username": "序号话", "password": "123", "group": "http://127.0.0.1:8000/api/v1/group/1/", "roles": [1, 2]},{"id": 2, "username": "民人接", "password": "123", "group": "http://127.0.0.1:8000/api/v1/group/2/", "roles": [3]}]-
小提示:
1、我们url中有一个pk参数,通过HyperlinkedIdentityField字段后面的别名参数生成url时,需要参数pk,默认是一当前user.pk传入的,显然不符合我们的要求,我们的group中只有两个组,用户只继承A组 2、对每一条数据进行序列化的时候,需要添加 ser = UserInfoSerializer(instance=users, many=True,context={'request':request})的context参数
-
-
升级版,修正url中的pk不对齐问题
class UserInfoSerializer(serializers.ModelSerializer): group = serializers.HyperlinkedIdentityField(view_name='gp', # 指定url别名 lookup_field='group_id', # 指定查找字段,可能是以他为参数,不能是group(对象) lookup_url_kwarg='pk') # 指定参数名 class Meta: model = models.UserInfo # fields = '__all__' fields = ['id', 'username', 'password', 'group', 'roles'] depth = 0 class UserinfoView(APIView): def get(self,request, *args, **kwargs): users = models.UserInfo.objects.all() ser = UserInfoSerializer(instance=users, many=True,context={'request':request}) # 需要写context print(ser.data) ret = json.dumps(ser.data, ensure_ascii=False) return HttpResponse(ret) # [{"id": 1, "username": "序号话", "password": "123", "group1":"http://127.0.0.1:8000/api/v1/group/1/", "roles": [1, 2]}, {"id": 2, "username": "民人接", "password": "123", "group1": "http://127.0.0.1:8000/api/v1/group/2/", "roles": [3]}]-
小提示:
1、在序列化类中定义group1字段,其以group.id作为url中的参数出入别名为'gp'的url中,再生成json返回数据 group1 = serializers.HyperlinkedIdentityField(view_name='gp',source='group.id') 2、源码中再HyperlinkedIdentityField中第一句: look_up = 'pk' # 表示默认以pk字段为参数传入 view_name = None
-
10.序列化源码解析
-
步骤一:代码分析
class UserInfoSerializer(serializers.ModelSerializer): group = serializers.HyperlinkedIdentityField(view_name='gp', lookup_field='group_id', lookup_url_kwarg='pk') class Meta: model = models.UserInfo # fields = '__all__' fields = ['id', 'username', 'password', 'group', 'roles'] depth = 0 class UserinfoView(APIView): def get(self,request, *args, **kwargs): users = models.UserInfo.objects.all() ser = UserInfoSerializer(instance=users, many=True,context={'request':request}) ret = json.dumps(ser.data, ensure_ascii=False) return HttpResponse(ret) # 以上述视图类为例子: 1、url路径匹配带CBV,再get中对UserInfoSeriazer进行实例化,去该类中找构造函数init,所以往上走 --> ModelSerializer类,任然没有,再往上走 --> Serializer类,没有 --> BaseSerializer中找到__init__(),同时还有__new__方法,且先执行new方法,进行判断many参数 def __new__(cls, *args, **kwargs): if kwargs.pop('many', False): # 分析是否是一个Queryst列表 return cls.many_init(*args, **kwargs) return super().__new__(cls, *args, **kwargs) # 对单个对象进行处理,从Field那获取空间self,然后执行自己的init方法 def __init__(self, instance=None, data=empty, **kwargs): self.instance = instance # 传入的单个对象 if data is not empty: self.initial_data = data self.partial = kwargs.pop('partial', False) self._context = kwargs.pop('context', {}) # 传入参数 kwargs.pop('many', None) super().__init__(**kwargs) 2、打开many_init()函数,里面返回了 def many_init(cls, *args, **kwargs): ... list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer) #调用ListSeralizer进行处理 return list_serializer_class(*args, **list_kwargs) 3、回到序列化后我们调用,ser.data,点击查看源码 3.1 @property def data(self): ret = super().data # 调用了BaseSerializer中的data属性 return ReturnDict(ret, serializer=self) # 返回一个有序字典 3.2、打开父类的data属性:(用于对_data进行返回) def data(self): ... if not hasattr(self, '_data'): if self.instance is not None and not getattr(self, '_errors', None): # 我们再去choices时使用过to_repersentions,用于获取对象中的值 self._data = self.to_representation(self.instance) ... return self._data 3.3、查找自己的self.to_representation方法,顶层是一个抛异常,最终我们在Serializer中找到了 def to_representation(self, instance): """ Object instance -> Dict of primitive datatypes. """ ret = OrderedDict() fields = self._readable_fields # 可读字段,进行了过滤了字段 for field in fields: # 对我们自定义的所有字段,循环 try: # 调用CharField.get_attribute方法,instance就是我们传入的对象 attribute = field.get_attribute(instance) except SkipField: continue check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute if check_for_none is None: ret[field.field_name] = None else: ret[field.field_name] = field.to_representation(attribute) return ret 3.4、找到父父父类Field中的get_attrbute(self,instance) def get_attribute(self, instance): """ Given the *outgoing* object instance, return the primitive value that should be used for this field. """ try: return get_attribute(instance, self.source_attrs) # 这里的source就是我们传入的source进行切割的列表,如'group.id'->[group,id], 我们再点开get_attrbute方法 ... 3.5、在进入更内层的get_attribute() def get_attribute(instance, attrs): """ Similar to Python's built in `getattr(instance, attr)`, but takes a list of nested attributes, instead of a single attribute. Also accepts either attribute lookup on objects or dictionary lookups. """ # 循环传入的source列表[group,title],或则['get_user_type_display'] for attr in attrs: try: if isinstance(instance, Mapping): instance = instance[attr] else: instance = getattr(instance, attr) # 拿到一个对象字段,给instance except ObjectDoesNotExist: return None if is_simple_callable(instance): # 判断是否是可以调用的如get_user_type_display try: instance = instance() # 执行函数 ... return instance-
小提示:
1、 many=True,将QuerySet列表交给ListSerializer处理 many=False ,将对象交给Serliazer处理 2、单个对象:self.to_representation Queryset时:self.to_representation
-
11.序列化请求数据校验
-
简单的从请求体中获取数据,并打印出form中的数据校验
class UserGroupSerializer(serializers.Serializer): titile = serializers.CharField(error_messages={'required':'标题不能为空'}) # 因为继承了Field,所以都有 class UserGourpView(APIView): def post(self,request, *args, **kwargs): print(request.body) # 必须再data之前使用,否则data后就没有body了,b'{\n"name":"alex"\n}' print(request.data) # 已经配置了解析器 {'name': 'alex'} ser = UserGroupSerializer(data=request.data) if ser.is_valid(): print(ser.validated_data) else: print(ser.errors) return HttpResponse('提交数据') # url中 urlpatterns = [ ... url(r'(?P<version>[v1|v2]+)/usergroup/$',views.UserGourpView.as_view()), ] # 运行结果 {'titile': [ErrorDetail(string='标题不能为空', code='required')]} -
自定义类进行内置校验器校验
class XXXValidator(object): def __init__(self, base): self.base = base # 传入的参数 def __call__(self, value): # 只要实例化就会触发__call__方法 if not value.startswith(self.base): # 必须以base开头 message = '标题必须以 %s 开头.' % self.base raise serializers.ValidationError(message) def set_context(self, serializer_field): # 执行验证之前调用,serializer_fields是当前字段对象 pass class UserGroupSerializer(serializers.Serializer): title = serializers.CharField(error_messages={'required':'标题不能为空'}, validators=[XXXValidator('中国'),]) class UserGourpView(APIView): def post(self,request, *args, **kwargs): print(request.body) # 必须再data之前使用,否则data后就没有body了,b'{\n"name":"alex"\n}' print(request.data) # 已经配置了解析器 {'name': 'alex'} ser = UserGroupSerializer(data=request.data) if ser.is_valid(): print(ser.validated_data) else: print(ser.errors) return HttpResponse('提交数据')-
补充:
当然可以通过函数进行内置校验器校验: def validate_title(value): if 'mjj' in value: raise serializers.ValidationError class UserGroupSerializer(serializers.Serializer): title = serializers.CharField(error_messages={'required':'标题不能为空'},validators=[validate_title,])
-
-
序列化的局部校验(钩子函数)
class UserGroupSerializer(serializers.Serializer): title = serializers.CharField(error_messages={'required':'标题不能为空'},validators=[]) def validate_title(self,value): # validate_字段名,用来局部验证,局部钩子 if 'mjj' in value: raise serializers.ValidationError return value class UserGourpView(APIView): def post(self,request, *args, **kwargs): print(request.body) # 必须再data之前使用,否则data后就没有body了,b'{\n"name":"alex"\n}' print(request.data) # 已经配置了解析器 {'name': 'alex'} ser = UserGroupSerializer(data=request.data) if ser.is_valid(): print(ser.validated_data) else: print(ser.errors) return HttpResponse('提交数据') -
序列化的全局钩子
class UserGroupSerializer(serializers.Serializer): title = serializers.CharField(error_messages={'required':'标题不能为空'},validators=[]) def validate_title(self,value): # validate_字段名,用来局部验证 if 'mjj' in value: raise serializers.ValidationError return value def validate(self, attrs): print(attrs) # OrderedDict([('title', 'mj')]) if attrs['title'] == '666': raise serializers.ValidationError #{'non_field_errors': [ErrorDetail(string='Invalid input.', code='invalid')]} return attrs class UserGourpView(APIView): def post(self,request, *args, **kwargs): print(request.body) # 必须再data之前使用,否则data后就没有body了,b'{\n"name":"alex"\n}' print(request.data) # 已经配置了解析器 {'name': 'alex'} ser = UserGroupSerializer(data=request.data) if ser.is_valid(): print(ser.validated_data) else: print(ser.errors) return HttpResponse('提交数据') -
使用extra_kwargs进行每个字段的扩展
class PasswordValidator(object): def __init__(self, base): self.base = str(base) def __call__(self, value): if value != self.base: message = 'This field must be %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 ModelUserSerializer(serializers.HyperlinkedModelSerializer): ll = serializers.HyperlinkedIdentityField(view_name='xxxx') tt = serializers.CharField(required=False) class Meta: model = models.UserInfo fields = "__all__" list_serializer_class = serializers.ListSerializer extra_kwargs = { # 可以对每个字段进行配置,甚至read_only 'user': {'min_length': 6}, 'pwd': {'validators': [PasswordValidator(666), ]}, 'url': {'view_name': 'xxxx'}, 'ut': {'view_name': 'xxxx'}, } class TestView(APIView): def get(self, request, *args, **kwargs): # # 序列化,将数据库查询字段序列化为字典 data_list = models.UserInfo.objects.all() ser = ModelUserSerializer(instance=data_list, many=True, context={'request': request}) return Response(ser.data) def post(self, request, *args, **kwargs): # 验证,对请求发来的数据进行验证 print(request.data) ser = ModelUserSerializer(data=request.data) if ser.is_valid(): print(ser.validated_data) else: print(ser.errors) return Response('POST请求,响应内容')

浙公网安备 33010602011771号