drf 03 序列化模块
目录
新建项目准备
import sys
# 标准输出流
sys.stdout.write('123\n')
sys.stdout.write('456\n')
sys.stdout.write('sdkfhsd四大皆空分段函数\n')
# 标准输入流
res = sys.stdin.readline() # 运行后在终端可输入内容以绿色显示
print(res)
# 标准错误流
sys.stderr.write('abc') # 在终端以红色字体显示括号内的内容
sys.stderr.write('def\n')
'''
标准输出流和标准错误流是一步执行的,并不是按照代码的先后顺序就先执行上面的输入流;
'''
项目数据库准备
# 将程序中数据转换成用于传输或存储的二进制字符串的过程叫做序列化;将硬盘中或者传输过来的二进制转换成程序能直接利用的数据叫做反序列化;简单的说,对象到字符串就是序列化,字符串到对象就是反序列化。
settings文件配置
# 注册
INSTALLED_APPS = [
# 'corsheaders', # 前后台跨域注册
'api',
# drf 使用时要注册,它使用了原生django的auth组件
'rest_framework',
]
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',, # 提交post请求,Jsonresponse做响应,需注销,或者前台携带csrf,如果是drf的response做响应,python基本数据类型都能返回给前台,前台什么提交方式,都不需要注销中间件csrf。
'corsheaders.middleware.CorsMiddleware', # 有跨域请求需注册
]
# 打开跨域
CORS_ORIGIN_ALLOW_ALL = True
# 自己定义请求头 在day69天
CORS_ALLOW_HEADERS = [
# 系统的
"accept",
"accept-encoding",
"authorization",
"content-type",
"dnt",
"origin",
"user-agent",
"x-csrftoken",
"x-requested-with",
# 自己定义的
"token",
"zhang",
]
# 链接mysql数据库
import pymysql # 不一定在__init__()内部加载,只要在启动django项目能加载的到配置的地方都可以,所以也可以写在settings文件中
pymysql.install_as_MySQLdb()
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'day67',
'PASSWORD':'123',
'USER':'root',
'CHARSET':'utf8'
}
}
# 时间配置
LANGUAGE_CODE = 'zh-hans' # 汉语
TIME_ZONE = 'Asia/Shanghai' # 东八区时间
USE_I18N = True
USE_L10N = True
USE_TZ = False #
# 暴露给用户的静态文件配置
STATIC_URL = '/static/'
# 媒体文件配置
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
# 后台http路径配置
BASE_URL = 'http://127.0.0.1:8000' # 项目上线后配置成具体路径
# drf框架自定义配置
REST_FRAMEWORK = {
# 全局配置渲染类,适用所有视图类
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
# 'rest_framework.renderers.BrowsableAPIRenderer', # 上线后尽量关闭
],
# 全局配置解析类,适用于所有视图类
'DEFAULT_PARSER_CLASSES': [
# 'rest_framework.parsers.JSONParser',
# 'rest_framework.parsers.FormParser',
# 'rest_framework.parsers.MultiPartParser'
],
# 异常模块:异常处理模块
# 'EXCEPTION_HANDLER':'rest_framework.views.exception_handler',
'EXCEPTION_HANDLER':'api.exception.exception_handler',
}
from django.db import models
class User(models.Model):
SEX_CHOICES = ((0,'女'),(1,'男'))
username = models.CharField(max_length=64,verbose_name='用户名',blank=True,unique=True) # 表示该字段在表中是唯一的,直接校验前台用户是否在数据库已存在
password = models.CharField(max_length=16,verbose_name='密码')
sex = models.IntegerField(choices=SEX_CHOICES,verbose_name='性别')
img = models.ImageField(upload_to='img',default='img/default.jpg',verbose_name='头像')
# 开发中,数据不会直接删除,通过字段控制
is_delete = models.BooleanField(default=False)
# 数据可数据入库,一般都会记录数据第一次入库时间,有时候还会记录最后一次更新时间
create_time = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
class Meta: # 配置类,给所属类提供配置信息的
db_table = 'User'
verbose_name_plural = '用户表'
def __str__(self):
return self.username # 不要在这里进行联表操作,比如,admin页面会崩溃
总路由文件urls.py
from django.conf.urls import url,include
from django.contrib import admin
from django.views.static import serve
from django.conf import settings # 先反射自己的settings文件,如果没有对应的配置,再使用系统默认的
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/',include('api.urls')),
url(r'media/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT}),
] # 起别名为path可以在系统源码的serve函数中了解使用
分路由文件urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'v1/users/$',views.UserV1APIView.as_view()),
url(r'v1/users/(?P<pk>\d+)/$',views.UserV1APIView.as_view()),
]
models.py文件
from django.db import models
class User(models.Model):
SEX_CHOICES = ((0,'女'),(1,'男'))
username = models.CharField(max_length=64,verbose_name='用户名',blank=True,unique=True)
password = models.CharField(max_length=16,verbose_name='密码')
sex = models.IntegerField(choices=SEX_CHOICES,verbose_name='性别')
img = models.ImageField(upload_to='img',default='img/default.jpg',verbose_name='头像')
# 开发中,数据不会直接删除,通过字段控制
is_delete = models.BooleanField(default=False)
# 数据可数据入库,一般都会记录数据第一次入库时间,有时候还会记录最后一次更新时间
create_time = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
class Meta: # 配置类,给所属类提供配置信息的
db_table = 'User'
verbose_name_plural = '用户表'
def __str__(self):
return self.username # 不要在这里进行联表操作,比如,admin页面会崩溃
异常文件exception.py
# 一定要在settings文件中将异常模块配置自己的异常处理函数
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.response import Response
from rest_framework import status
def exception_handler(exc, context):
response = drf_exception_handler(exc, context)
detail = '%s - %s - %s' % (context.get('view'), context.get('request').method, exc)
if not response: # 服务端错误
response = Response({'detail': detail}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
else:
response.data = {'detail': detail}
# 核心:要将response.data.get('detail')信息记录到日志文件
# logger.waring(response.data.get('detail'))
import sys
sys.stderr.write('异常:%s\n'%response.data.get('detail'))
# print(response.data.get('detail'))
return response
自定义序列化过程
from rest_framework.views import APIView
from rest_framework.response import Response
from django.conf import settings
from . import models
class UserV1APIView(APIView):
# 单查群查
def get(self,request,*args,**kwargs):
pk = kwargs.get('pk')
if pk:
user_obj = models.User.objects.filter(is_delete=False,pk=pk).values('username', 'sex', 'img').first()
if not user_obj:
return Response({
'status':0,
'msg':'pk error',
},status=400)
user_obj['img'] = '%s%s%s' % (settings.BASE_URL, settings.MEDIA_URL, user_obj.get('img'))
# print(user_list)
return Response({
'status': 0,
'msg': 'ok',
'result': user_obj
})
else:
user_query = models.User.objects.filter(is_delete=False).values('username','sex','img')
# print(user_query)
for user_dic in user_query:
user_dic['img'] = '%s%s%s' % (settings.BASE_URL, settings.MEDIA_URL, user_dic.get('img'))
print(user_dic['img'])
user_list = list(user_query)
# print(user_list)
return Response({
'status':0,
'msg':'ok',
'result':user_list
})
serializer序列化与反序列化过程
drf 序列化过程
视图类序列化过程:
1)ORM操作得到数据
2)将数据序列化成可以返回给前台的数据
3)返回数据给前台
序列化总结:
1)设置序列化字段,字段名与字段类型要与处理的model类属性名对应(只参与序列化的类型不需要设置条件)
2)model类中有的字段,但在序列化中没有对应字段,该类字段不参与序列化
3)自定义序列化字段(方法一),字段类型为SerializerMethodField(),值有 get_自定义字段名(self, model_obj) 方法提供,
一般值都与参与序列化的model对象(model_obj)有关
# serializers.py 序列化过程
from rest_framework import serializers
from django.conf import settings
class UserSerializers(serializers.Serializer):
# 1)字段名与字段类型要与处理的model.py文件内表类字段和字段类型对应
# 2)不提供的字段,不参与序列化给前台
username = serializers.CharField() # 通过反射取值
# sex = serializers.IntegerField()
# 3)可以自定义序列化字段,采用方法序列化(不建议自定义字段名与数据库字段名重名,
# 会覆盖数据库字段名),由“get_字段名”方法的返回值提供给前台字段值
gender = serializers.SerializerMethodField()
def get_gender(self,obj):
# print(obj) 每走一次Serializer实例化方法,循环出一个User object
return obj.get_sex_display()
icon = serializers.SerializerMethodField()
def get_icon(self,obj):
return '%s%s%s'%(settings.BASE_URL,settings.MEDIA_URL,obj.img)
# views.py
from . import serializers
class UserV2APIView(APIView):
def get(self,request,*args,**kwargs):
pk = kwargs.get('pk')
if pk:
user_obj = models.User.objects.filter(is_delete=False,pk=pk).first()
# if not user_obj:
# # return Response({
# # 'status': 0,
# # 'msg': 'pk error',
# # }, status=400)
# 传入的user_obj对象不管是否有值都不会影响序列化,返回给前台的是空值
user_ser = serializers.UserSerializers(user_obj,many=False) # many默认为False
# print(user_ser)
"""
UserSerializers(<User: Jerry>):
username = CharField()
gender = SerializerMethodField()
icon = SerializerMethodField()
"""
# print(user_ser.data) # {'username': 'Jerry', 'gender': '女', 'icon': 'http://127.0.0.1:8000/media/img/22.jpg'}
return Response({
'status': 0,
'msg': 'ok',
'result': user_ser.data
})
else:
# 将对前台提供的 字段,进行整个序列化过程封装,形成序列化类
user_query = models.User.objects.filter(is_delete=False).all()
# 调用源码BaseSerializer类的__init_-方法,进行初始化,并将纯如的值,让初始化方法的data
# 形参接手,所以序列化之后可以通过对象点data获得序列化之后的结果
user_ser = serializers.UserSerializers(user_query,many=True) # 多个对象设置为True
# print(type(user_ser)) # <class 'rest_framework.serializers.ListSerializer'>
user_list = user_ser.data
# print(user_list)
"""
[OrderedDict([('username', 'TOm'),
('gender', '男'),
('icon', 'http://127.0.0.1:8000/media/img/44.jpg')])]
"""
return Response({
'status':0,
'msg':'ok',
'result':user_list
})
反序列化过程
1) 从请求对象中获取前台提交的数据
2)交给序列化类完成反序列化(数据校验)
3)借助序列化类完成数据入库
4)返回给前台处理结果
反序列化总结:
1)系统校验字段与自定义校验字段定义没有区别:字段 = serializers.字段类型(条件)
2)自定义校验字段是不能直接入库的,需要设置入库规则,或将其移除不入库(这类字段就是参与全局校验用的)
3)所有字段都可以设置对应局部钩子进行校验,钩子方法 validate_字段名(self, 字段值value)
规则:成功直接返回value,失败抛出校验失败信息ValidationError('错误信息')
4)一个序列化类存在一个全局钩子可以对所有字段进行全局校验,钩子方法 validate(self, 所有字段值字典attrs)
规则:成功直接返回attrs,失败抛出校验失败信息ValidationError({'异常字段', '错误信息'})
5)重写create方法实现增入库,返回入库成功的对象
6)重写update方法实现改入库,返回入库成功的对象
from rest_framework.views import APIView
from rest_framework.response import Response
from . import serializers
class UserV2APIView(APIView):
# 单增
def post(self,request,*args,**kwargs):
print(request.data)
request_data = request.data
user_ser = serializers.UserDeSerializers(data=request_data)
if user_ser.is_valid():
user_obj = user_ser.save()
return Response({
'status':0,
'msg':'ok',
'results':serializers.UserSerializers(user_obj).data
})
else:
return Response({
'status':1,
'msg':user_ser.errors,
})
# serializers.py
from api import models
class UserDeSerializers(serializers.Serializer):
#系统校验字段
username = serializers.CharField(min_length=3,max_length=10,error_messages={
# 'MIN_LENGTH':'太短了',
# 'MXA_LENGTH':'太长了',
'min_length': '太短了',
'max_length': '太长了',
})
password = serializers.CharField(min_length=3,max_length=16)
sex = serializers.BooleanField(required=False) # required指定false,表明该字段不是必填字段
# 自定义校验字段,从设置语法上与系统字段没有任何区别,但是这些字段不入库,需要在全局钩子中取出
re_password = serializers.CharField(min_length=3,max_length=16)
# 局部钩子
"""
方法名就是validate校验的字段名(self,校验字段的数据)
校验规则:成功直接返回values,失败抛出校验失败信息
"""
def validate_username(self, values):
if 'z'in values:
raise serializers.ValidationError('名字不能有z')
return values
# 全局钩子:
# 方法就是 validate(self, 所有的校验数据)
# 校验规则:成功直接返回attrs,失败抛出校验失败信息
def validate(self, attrs):
print(attrs) # OrderedDict([('username', 'rose'), ('password', '123'), ('re_password', '123')])
password=attrs.get('password')
re_password = attrs.pop('re_password')
if password != re_password:
raise serializers.ValidationError({"re_password":'两次密码不一致!'})
return attrs
# 在视图类中调用序列化类的save方法完成入库,Serializer类能做的增入库走create方法,改入库走update方法
# 但Serializer没有提供两个方法的实现体
def create(self, validated_data): # views.py文件中user_ser.save()源码中调用的create方法,首先调用我们自己写的
return models.User.objects.create(**validated_data)
# instance要被修改的对象,validated_data代表校验后用来改instance的数据
def update(self, instance: models.User, validated_data):
# 用户名不能被修改
validated_data.pop('username')
models.User.objects.filter(pk=instance.id).update(**validated_data)
return instance
ModelSerializers序列化与反序列化过程
ModelSerializer类序列化与反序列化总结:
1)序列化类继承ModelSerializer,所以需要在配置类Meta中进行配置
2)model配置:绑定序列化相关的Model表
3)fields配置:采用 插拔式 设置所有参与序列化与反序列化字段
4)extra_kwargs配置:
划分系统字段为三种:只读(read_only)、只写(write_only)、可读可写(不设置)
字段是否必须:required
选填字段:在extra_kwargs进行配置,但不设置required,且有默认值
5)自定义序列化字段:
第一种(不提倡):在序列化类中用SerializerMethodField()来实现
第二种(提倡):在模型类中用@property来实现,可插拔
6)自定义反序列化字段:
同Serializer类,且规则只能在此声明中设置,或是在钩子中设置,在extra_kwargs中对其设置的无效
自定义反序列化字段与系统字段,设置规则一样,所以必须设置 write_only
7)局部钩子,全局钩子同Serializer类
8)不需要重写create和update方法
class UserV3APIView(APIView):
# 单查群查
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
user_obj = models.User.objects.filter(is_delete=False, pk=pk).first()
if not user_obj:
return Response({
'status': 1,
'msg': 'pk error',
}, status=400)
user_ser = serializers.UserModelSerializer(user_obj, many=False)
return Response({
'status': 0,
'msg': 'ok',
'results': user_ser.data
})
else:
user_query = models.User.objects.filter(is_delete=False).all()
user_ser = serializers.UserModelSerializer(user_query, many=True)
return Response({
'status': 0,
'msg': 'ok',
'results': user_ser.data
})
# 单增
def post(self, request, *args, **kwargs):
user_ser = serializers.UserModelSerializer(data=request.data)
if user_ser.is_valid():
# 入库
user_obj = user_ser.save()
return Response({
'status': 0,
'msg': 'ok',
'results': serializers.UserModelSerializer(user_obj).data
})
else:
return Response({
'status': 1,
'msg': user_ser.errors,
})
# serializers.py
class UserModelSerializer(serializers.ModelSerializer):
# 第一种自定义序列化字段:该字段必须在fields中设置
# gender = serializers.SerializerMethodField()
# def get_gender(self, obj):
# return obj.get_sex_display()
# 自定义反序列化字段同Serializer类,且规则只能在此声明中设置,或是在钩子中设置,
# 在extra_kwargs中对其设置的无效
# 注:自定义反序列化字段与系统字段,设置规则一样,所以必须设置 write_only
re_password = serializers.CharField(min_length=3, max_length=16, write_only=True)
class Meta:
model = models.User
# fields采用 插拔式 设置所有参与序列化与反序列化字段
fields = ('username', 'gender', 'icon', 'password', 'sex', 're_password')
extra_kwargs = {
'username': { # 系统字段不设置read_only和write_only,默认都参加
'min_length': 3,
'max_length': 10,
'error_messages': {
'min_length': '太短',
'max_length': '太长'
}
},
'gender': {
'read_only': True, # 自定义的序列化字段默认就是read_only,且不能修改,可以省略
},
'password': {
'write_only': True,
},
'sex': { # 像sex有默认值的字段,为选填字段('required': True可以将其变为必填字段)
'write_only': True,
# 'required': True
}
}
# 局部全局钩子同Serializer类,是与 Meta 同缩进的
def validate_username(self, value):
if 'g' in value.lower():
raise serializers.ValidationError('名字中不能有g')
return value
def validate(self, attrs):
password = attrs.get('password')
re_password = attrs.pop('re_password')
if password != re_password:
raise serializers.ValidationError({'re_password': '两次密码不一致'})
return attrs
# create和update方法不需要再重写,ModelSerializer类已提供,且支持所有关系下的连表操作
# 第二种自定义序列化字段(插拔式,提倡使用),在models.py文件中将序列化字段以方法的形式写在对应类内部,并在fields内写入该字段,利用反射即可实现插拔式
@property
def gender(self):
return self.get_sex_display()
@property
def icon(self):
from django.conf import settings
return '%s%s%s' % (settings.BASE_URL, settings.MEDIA_URL, self.img)