drf之序列化组件(一):Serializer
序列化组件:Serializer、ModelSerializer、ListModelSerializer
Serializer 偏底层 ModelSerializer 重点 ListModelSerializer 辅助群改
1.Serializer组件
(1)准备
(1)models.py中
class User(models.Model): SEX_CHOICES = [ [0,”男”], #前面是0还是”0”和IntergerField还是CharField有关 [1,”女”] #前面是填入的,后面是显示的 ] name = models.CharField(max_length=64) pwd = models.CharField(max_length=32) phone = models.CharField(max_length=11, null=True, default=None) sex = models.IntegerField(choices=SEX_CHOICES, default=0) icon = models.ImageField(upload_to=”icon”, defult=’icon/defult.png’) #需要在项目base目录下新建media文件夹,然后在media下建立icon文件夹
(默认会找media下的icon文件夹) class Meta: db_table = ‘yq_user’ verbose_name = “用户表” verbose_name_plural = verbose_name def __str__(self): return “%s”%self.name
(2)在admin.py下注册User
admin.site.register(models.User)
(3)在shell中迁移
makemigrations、migrate
get请求是要序列化,将后台数据返回给前台;post请求要反序列化,将前台数据给后台存入数据库中,同时也要序列化对象展示数据给前台。
(4)配置媒体资源
1)在settings.py里配置media路径
MEDIA_ROOT = os.path.join(BASE_DIR, ‘media’)
2)在项目文件下的urls.py中
#media在根路由配置
from django.views.static import serve from django.conf import setting urlpatterns = [ ... url(r”^media/(?P<path>.*)”, serve, {‘document_root’: setting.MEDIA_ROOT}) #正则匹配时不要写$,可以一层层找 ]
3)数据库再迁移下
用户对象(model对象或者query_set对象)、用户对象列表不能直接返回给前台,要序列化下对象。因此要用Serializer。
(2)序列化
(1)配置路由
urlpatterns = [ url(r'^users/$', views.User.as_view()), url(r'^users/(?P<pk>.*)/$', views.User.as_view()), ]
(2)自定义序列化类。在应用文件夹下新建serializers.py文件。
序列化组件的工作方式与django的forms组件相似
from rest_framework import serializers class UserSerailizer(serializers.Serializer): #字段的类型和定义时一致,字段为想要返回给前台的内容(pwd不用返回就不写) name = serializers.CharField() #序列化的括号里不需加条件 phone = serlializers.CharField() #sex = serializers.IntegerField() #icon = serializers.ImageField() #由于sex默认返回的是0、1数字给前台,icon返回的不是全链接,因此要自定义序列化属性:定制返回的内容 gender = serializers.SerializerMethodField() #可以叫gender,也可以叫sex,不一定与字段名保持一致 def get_gender(self, obj): #必须以get开头,后面也必须和上面的gender对应 #self是serializer对象,obj是模型类对象 return obj.get_sex_display() #get_xxx_display() 表示取到选项值 #在settings.py中配置下MEDIA_URL = /media/ from untitled1 import settings img = serializer.SerializerMethodField() def get_img(self, obj): return “%s%s%s”%(r”http://127.0.0.1:8000”, settings.MEDIA_URL, str(obj.icon)) #obj.icon不能直接作为数据返回,类型是ImageFile类型,可以通过str转化为str类型。
(3)在views.py中返回对象序列化后的json数据
from . import serializers from rest_framework.views import APIView from rest_framework.response import Response class User(APIView): def get(self, request, *args, **kwargs): pk = kwargs.get('pk') #通过有名分组匹配到的路径参数 if pk: #单查 try: user_obj = models.User.objects.get(pk=pk) #取到model对象 #将对象序列化取得dict数据 user_ser_data = serializers.UserSerializer(user_obj).data return Response({ "status": 0, "msg": "ok", "results": user_ser_data }) except: return Response({ "status": 2, "msg": "no user", }) else: #群查 user_obj_list = models.User.objects.all() #将对象列表序列化 user_ser_data = serializers.UserSerializer( user_obj_list,many=True).data return Response({ "status": 0, "msg": "ok", "results":user_ser_data })
总结:
序列化ser:
1)序列化给前台的字段个数可以由后台自己决定,但是提供的序列化字段的一定要和数据库的字段的类型和名字一致。
2)自定义属性名:为了返回自定义的字段的值。属性名随意,值由固定的命名规范方法提供:get_属性名(self, obj)。obj为参与序列化的model或者query_set对象。返回值就是自定义序列化属性的值。
view:
1)从数据库中将要序列化给前台的model对象,或是多个model对象查询出来
user_obj = models.User.objects.get(pk=pk)或者
user_objs_list = models.User.objects.all()
2)将对象交给序列化处理,产生序列化对象,如果序列化的是多个数据,要设置many=True
user_ser = serializers.UserSerializer(user_obj)或者
user_ser_list = serializers.UserSerializer(user_objs_list, many=True)
3)序列化对象.data就是可以返回给前台的序列化数据,然后return Response返回
(3)反序列化
反序列化考虑的问题:
1)哪些字段必须反序列化
2)字段都有哪些安全校验
3)哪些字段需要提供额外的安全校验
4)哪些字段间存在联合校验
反序列化字段全都是用来入库的,不会出现自定义方法属性,但会出现设置校验规则(系统校验、局部钩子和全局钩子)的自定义属性(如re_pwd)
(1)还是在刚才的serializers.py文件中
class UserDeserializer(serializer.Serializer): name = serializers.CharField( required=True, max_length=64, #自定义检验规则 min_length=3, error_message = { #错误信息,不给是默认的 ‘max_length’:’太长’, ‘min_length’:’太短’, }) pwd= serializers.CharField(required=True) phone = serializers.CharField(required=False) sex = serializers.IntegerField(required=False) #自定义有校验规则的反序列化字段。不存入库,只是用来校验 re_pwd = serializers.CharField(required=True) #用来判断两次输入密码一样 #局部钩子:validate_要校验的字段名(self, 当前要检验字段的值) #校验规则:校验成功返回原值,失败返回异常 #局部钩子校验某个字段 def validate_name(self, value): if ‘j’ in value.lower(): #名字中不能出现j #from rest_framework import exceptions raise exceptions.ValidationError(“名字非法”) return value #必须返回,校验成功返回value,失败抛异常 #全局钩子:固定写法validate(self, attrs)。attrs为系统与局部钩子校验通过的所有数据。(系统和全局之间还有个局部钩子) #校验时需要用到多个字段。比如两次密码输入一致 #校验规则:校验成功返回原值,失败返回异常 def validate(self, attrs): pwd = attrs.get(‘pwd’) #只是取得 re_pwd = attrs.pop(‘re_pwd’) #取得并且删除(不需要入库) if pwd != re_pwd: raise exceptions.ValidationError({“pwd“: “两次密码不一致”}) return attrs #必须返回,校验成功返回attrs,失败抛异常 #要完成新增,需在serializers.py中的自定义反序列化类中重新create方法。 def create(self, validated_data): #在views.py中校验后,save方法调用时内部会调用create。 #在所有校验规则完毕之后,数据(validated_data是已经校验好的数据)可以直接入库 #from . import models return models.User.objects.create(**validated_data)
(2)在views.py下 的User类下写post方法(对post提交的数据反序列化)
def post(self, request, *args, **kwargs): request_data = request.data #字典 #数据是否合法(增加对象必须是一个字典数据且有值) if not isinstance(request_data, dict) or not request_data: return Response({ "status": 1, "msg": "数据有误", }) #数据类型合法,但数据内容不一定合法,需要校验数据,校验的(参与反序列化)数据需要赋值给data book_ser = serializers.UserDeserializer(data=request_data) #必须关键字data赋值 #序列化对象调用is_valid完成校验,校验失败信息存储在序列化对象.errors中 if book_ser.is_valid(): book_obj = book_ser.save() #校验通过,完成新增(save的返回值就是完成校验后create返回的对象) return Response({ "status": 0, "msg": "ok", "results":serializers.UserSerializer(book_obj).data #新增和修改,都要 将反序列化的对象 再序列化 返回给其前台 }) else: #校验没通过(校验的错误信息存在book_ser的errors中) return Response({ "status": 1, "msg": book_ser.errors, })
反序列化总结:
视图类中:
1)把要校验的数据交给序列化类,由data接收;
book_ser = serializers.UserDeserializer(data=request_data)
2)走is_valid校验;
if book_ser.is_valid():
3)校验成功走save方法,校验失败返回errors。
book_obj = book_ser.save()
序列化类中:
1)设置必填与选填序列化字段,设置校验规则
2)为需要额外校验的字段提供局部钩子函数,如果某些字段(如验证码)不入库且不参与全局钩子,可以将值取出pop校验(一般在全局钩子里)
3)为有联合关系的字段们提供全局钩子。如果某些字段不入库,也取出pop校验(如二次输入的密码)
4)重写create方法,完成校验通过的字段入库,得到新增的对象。
ser = 自定义序列化类(instance=模型类对象, data=empty, **kwargs) #序列化时传入模型类对象(instance=可以不写,直接写对象),不需要写data(data是反序列化才有);**kwargs比如当对象是个列表时,可以加个many=True参数。
ser = 自定义反序列化类(instance=None, data=数据) #将要被反序列化的前端传来的数据传入data中。