序列化组件
"""
序列化:对象 可以序列化后 用于网络传输
反序列化:网络传输来的数据 反序列化成 对象用于使用
"""
准备工作
路由
# 主路由 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
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/', include('api.urls')),
url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})
]
# 子路由 api/urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^users/$', views.UserAPIView.as_view()),
url(r'^users/(?P<pk>.*)/$', views.UserAPIView.as_view()),
]
模型:api/models.py
class User(models.Model):
# choices的字段,直接获取只能获取 0 | 1 | 2
# 想获取 值后 的映射关系 男 | 女 | 哇塞 用 get_字段名_display()
# eg:user_obj.get_sex_diaplay()
SEX_CHOICES = [
(0, '男'),
(1, '女'),
(2, '哇塞')
]
username = models.CharField(max_length=64, unique=True)
password = models.CharField(max_length=64)
sex = models.IntegerField(choices=SEX_CHOICES, default=0)
icon = models.ImageField(upload_to='icon', default='icon/default.png')
create_time = models.DateTimeField(auto_now_add=True, null=True)
is_delete = models.BooleanField(default=False)
class Meta:
db_table = 'old_boy_user'
verbose_name = '用户'
verbose_name_plural = verbose_name
def __str__(self):
return self.username
配置:settings.py
# 注册
INSTALLED_APPS = [
# ...
'rest_framework',
]
# 国际化配置 关于数据库的时间的配置 这么写就会获取准确的上海时间了
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
# media资源配置
MEDIA_URL = '/media/' # 序列化media文件夹下的资源,会默认添加MEDIA_URL
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
一 简单认识序列化
序列化类:api/serializers.py
from django.conf import settings
from . import models
# 序列化
class UserSerializer(serializers.Serializer):
# 1)参与序列化的属性名必须与model类的属性字段名相同
# 2)不需要序列化的属性字段在序列化类中不需要声明
# 3) 方法序列化字段,不需要在model类中有字段属性对应,值来源于 get_字段 方法
# 序列化字段可以完全自定义,也可以与model类中有字段属性相同
# 1)
username = serializers.CharField()
# 2)
# password = serializers.CharField()
sex = serializers.IntegerField()
# icon = serializers.ImageField()
# 3)
gender = serializers.SerializerMethodField()
def get_gender(self, user_obj):
return user_obj.get_sex_display()
icon = serializers.SerializerMethodField()
def get_icon(self, user_obj):
icon_url = 'http://127.0.0.1:8000{}{}'.format(settings.MEDIA_URL, user_obj.icon)
return icon_url
序列化视图类:api/views.py
from rest_framework.views import APIView
from utils_home.response import APIResponse
from . import models, serializers
class UserAPIView(APIView):
# 序列化对象:
# UserSerializer(序列化数据, many)
# 1)序列化数据可以为User类的单个对象,many=False(默认,可以省略不写)
# 2)序列化数据可以为状态User类的多个对象的单列集合
# [] () {} QuerySet,此时many=True必须明确
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk: # 获取单个资源
user_obj = models.User.objects.filter(pk=pk).first()
if not user_obj:
return APIResponse(1, 'pk error')
user_obj_data = serializers.UserSerializer(user_obj).data
return APIResponse(0, 'ok', results=user_obj_data)
# 获取多个资源
user_query = models.User.objects.all()
user_ser = serializers.UserSerializer(user_query, many=True)
user_list_data = user_ser.data
return APIResponse(0, 'get ok', results=user_list_data)
二 简单认识反序列化
反序列化类:api/serializers.py
from django.conf import settings
from . import models
# 反序列化
class UserDeserializer(serializers.Serializer):
# 1)在字段类型后定义基础校验规则
# 2)基础校验完毕,可以在局部钩子方法中进行单个字段校验规则的扩展
# 3)在局部钩子校验完毕,在全局钩子中完成多个字段协同校验
# 4)如果需要提供数据的增加功能,需要重写create方法,完成数据的入库
# 1)默认校验
username = serializers.CharField(
min_length=3,
error_messages={
'min_length': '用户名太短'
}
)
password = serializers.CharField(
min_length=3,
error_messages={
'min_length': '密码太短'
}
)
re_password = serializers.CharField(
min_length=3,
required=True,
error_messages={
'min_length': '确认密码太短',
'required': '确认密码不能为空'
}
)
# 有默认值的字段,前台可能提供,可能不通过,这样的字段用required=False处理
sex = serializers.IntegerField(required=False)
# 2)局部钩子: validate_字段名(self, 字段值)
def validate_username(self, value):
if 'sb' in value:
raise serializers.ValidationError('用户名包含敏感词汇')
return value
# 3)全局钩子:validate(self, 所有字段值)
def validate(self, attrs):
print(attrs)
password = attrs.get('password')
# 校验通过后,要完成数据的增加,该增加不参与,要从数据们中剔除
re_password = attrs.pop('re_password')
if password != re_password:
raise serializers.ValidationError({'re_password': '两次密码不一致'})
return attrs
# 4)完成model类对象的增加,必须重写create方法
def create(self, validated_data):
try:
return models.User.objects.create(**validated_data)
except:
raise IOError('数据库入库失败') # 数据库异常在异常模块中完善
序列化视图类:api/views.py
from rest_framework.views import APIView
from utils_home.response import APIResponse
from . import models, serializers
class UserAPIView(APIView):
# 反序列化请求数据
# UserDeserializer(data=反序列化数据, many=False)
# 1)反序列化数据可以为单个数据字典,many=False或者不写
# 2)反序列化数据可以为存放数据字典的单列集合,many=True必须明确(后面讲)
# 3)反序列化数据必须赋值给data关键字参数,才能进行数据校验
# 4)序列化对象.is_valid(raise_exception=True)校验失败会自动返回错误信息给前台
# 5)if 序列化对象.is_valid(): 可以自定义校验成功与失败分支的返回结果
# 成功可以完成新增或修改,失败错误信息在 序列化对象.errors 中
def post(self, request, *args, **kwargs):
request_data = request.data
print(request_data)
user_ser = serializers.UserDeserializer(data=request_data)
# 自动返回错误信息
# user_ser.is_valid(raise_exception=True)
if user_ser.is_valid(): # 自定义处理校验成功的逻辑
user_obj = user_ser.save()
return APIResponse(0, 'ok',
results=serializers.UserSerializer(user_obj).data
)
else: # 自定义返回错误信息
return APIResponse(1, 'failed', results=user_ser.errors)
三 序列化与反序列化共存(重点)
路由:api/urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^users/$', views.UserAPIView.as_view()),
url(r'^users/(?P<pk>.*)/$', views.UserAPIView.as_view()),
# 整合序列化与反序列
url(r'^v2/users/$', views.UserV2APIView.as_view()),
url(r'^v2/users/(?P<pk>.*)/$', views.UserV2APIView.as_view()),
]
序列化层:api/serializers.py
from rest_framework import serializers
from django.conf import settings
from . import models
# 序列化与反序列化整合
class UserV2Serializer(serializers.Serializer):
# 1)参与序列化与反序列化的字段(包括自定义字段)都必须明确
# 2)通过 read_only=True 表明该字段只参与序列化
# 3)通过 write_only=True 表明该字段只参与反序列化
# 4)参与反序列化,但是是选填字段(前台可以提供或不提供),用 required=False 处理
# 5)要完成数据库增加,需要重写 create 方法
# 6)要完成数据库更新,需要重写 update 方法
username = serializers.CharField(min_length=3)
# 只参与反序列化 write_only=True
password = serializers.CharField(write_only=True, min_length=3)
sex = serializers.IntegerField(write_only=True, required=False)
re_password = serializers.CharField(write_only=True, min_length=3, required=True)
# 只参与序列化 read_only=True
gender = serializers.SerializerMethodField(read_only=True)
def get_gender(self, user_obj):
return user_obj.get_sex_display()
icon = serializers.SerializerMethodField(read_only=True)
def get_icon(self, user_obj):
icon_url = 'http://127.0.0.1:8000{}{}'.format(settings.MEDIA_URL, user_obj.icon)
return icon_url
def validate_username(self, value):
if 'sb' in value:
raise serializers.ValidationError('用户名包含敏感词汇')
return value
def validate(self, attrs):
print('序列化内:', self.context)
password = attrs.get('password')
re_password = attrs.get('re_password')
if password:
if re_password:
attrs.pop('re_password')
if password != re_password:
raise serializers.ValidationError({'re_password': '两次密码不一致'})
else:
raise serializers.ValidationError({'re_password': '密码必须确认'})
return attrs
# 增
def create(self, validated_data):
try:
return models.User.objects.create(**validated_data)
except:
raise IOError('数据库入库失败')
# 改
def update(self, instance, validated_data):
# instance:是自定义传入的要更新的原数据(pk | obj | queryset)
# validated_data:是校验通过后的新数据
# instance的值外部反序列化传入要更新的自定义标识决定
instance.update(**validated_data)
return instance.first()
视图层:api/views.py
from rest_framework.views import APIView
from utils_home.response import APIResponse
from . import models, serializers
class UserV2APIView(APIView):
# 单取、群取
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
user_obj = models.User.objects.filter(pk=pk, is_delete=False).first()
if not user_obj:
return APIResponse(1, 'pk error')
user_obj_data = serializers.UserV2Serializer(user_obj).data
return APIResponse(0, 'ok', results=user_obj_data)
user_query = models.User.objects.filter(is_delete=False).all()
user_ser = serializers.UserV2Serializer(user_query, many=True)
user_list_data = user_ser.data
return APIResponse(0, 'ok', results=user_list_data)
# 单增
def post(self, request, *args, **kwargs):
request_data = request.data
print(request_data)
user_ser = serializers.UserV2Serializer(data=request_data)
if user_ser.is_valid():
user_obj = user_ser.save()
return APIResponse(0, 'ok',
results=serializers.UserV2Serializer(user_obj).data
)
else:
return APIResponse(1, 'failed', results=user_ser.errors)
# 单整体改
def put(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if not pk:
return APIResponse(1, 'pk error')
user_query = models.User.objects.filter(pk=pk, is_delete=False)
if not user_query:
return APIResponse(1, 'user error')
# 第一种:user_query完成数据的更新
# user_query = models.User.objects.filter(pk=pk)
# user_query.update(**kwargs)
# 第二种:user_obj完成数据的更新
# user_obj = models.User.objects.filter(pk=pk).first() # type: models.User
# user_obj.username = 'new_username'
# ...
# user_obj.save()
request_data = request.data
user_ser = serializers.UserV2Serializer(instance=user_query, data=request_data)
if user_ser.is_valid():
# save的返回值是由update内部自定义的返回值决定,
# save如果有instance的话就走update,如果没有就走create
user_obj = user_ser.save()
return APIResponse(0, 'ok',
results=serializers.UserV2Serializer(user_obj).data
)
else:
return APIResponse(1, 'failed', user_ser.errors)
# 单局部改
def patch(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if not pk:
return APIResponse(1, 'pk error')
user_query = models.User.objects.filter(pk=pk, is_delete=False)
if not user_query:
return APIResponse(1, 'user error')
request_data = request.data
# 局部数据修改,设置 partial=True 只校验了更新数据的字段 以及更新字段的局部钩子和全局钩子
# 视图类给序列化类传递自定义参数 context=值 值一般用字典
# 在序列化类的钩子函数中用self.context获取传入的值
user_ser = serializers.UserV2Serializer(context={'arg': '我是视图的'}, partial=True, instance=user_query, data=request_data)
if user_ser.is_valid():
user_obj = user_ser.save()
return APIResponse(0, 'ok',
results=serializers.UserV2Serializer(user_obj).data
)
else:
return APIResponse(1, 'failed', user_ser.errors)
# 单删
def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if not pk:
return APIResponse(1, 'pk error')
user_obj = models.User.objects.filter(pk=pk, is_delete=False).first()
if not user_obj:
return APIResponse(1, '删除失败')
user_obj.is_delete = True
user_obj.save()
return APIResponse(0, '删除成功')