DRF--------(二)
一.封装Response(APIResponse内部把数据状态码,返回信息,响应结果,甚至其他信息都丢到
data字典中,返回响应数据时正常返回参数值即可)
比如 return APIResponse(0, 'get ok', results=user_list_data,token='1232)
from rest_framework.response import Response """ # 封装前 Response({ 'status': 0, 'msg': 'ok', 'results': [], 'token': '' }, headers={}, status=200, content_type="") # 封装后 APIResponse(0, 'ok', results, status, headers, content_type) """ class APIResponse(Response): def __init__(self, data_status, data_msg, results=None, status=None, headers=None, content_type=None, **kwargs): data = { 'status': data_status, 'msg': data_msg } if results: data['results'] = results data.update(kwargs) super().__init__(data=data, status=status, headers=headers, content_type=content_type)
二.序列化组件
序列化: 对象 可以序列化后 用于网络传输 (一般在get请求中)
反序列化:网络传输来的数据 反序列化成 对象用于使用 (一般在post请求中)
a.路由
# 主路由 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()), ]
b. model.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
c .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')
序列化类 serializers.py
(1)参与序列化的属性名必须与model类的属性字段名相同, 除非用source改名
比如: name = serializers.CharField(source='username')
(2)不需要序列化的属性字段在序列化类中不需要声明
(3)方法序列化字段,不需要与model类中已有字段属性对应,值来源于 get_字段 方法,
该方法的第二个参数是对应model类的obj对象,对于在返回性别的choice指向的值时,
可用 user_obj.get_sex_display()的方式取出,对于返回图片要拼接media的路径
注:序列化字段可以完全自定义,也可以与model类中已有字段属性相同
from rest_framework import serializers
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
序列化数据可以为User类的单个对象,many=False(默认,可以省略不写)
(2) 序列化数据可以为状态User类的多个对象的单列集合(包括[ ] ,( ) ,集合,queryset对象等),many = True必须明确
(3)判读get请求中是单条还是多条数据(即判断pk是否存在)
(4) user_obj_data = serializers.UserSerializer(user_obj).data: 实例化时 instance=obj/query_set,可不写,返回的results必须接受serializer对象.data数据
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)
三.反序列化(一般用于post请求)
1)在字段类型后定义基础校验规则(包括max_length,min_length,error_messeges等)
有默认值的字段,前台可能提供,可能不通过,这样的字段用required=False处理,即序列化,但不校验
2)基础校验完毕,可以在局部钩子方法中进行单个字段校验规则的扩展 :
validate_字段名(self, value),value表示该字段的字段值,校验通过要把value返回;
校验不通过, raise serializers.ValidationError('用户名包含敏感词汇')
3)在局部钩子校验完毕,在全局钩子中完成多个字段协同校验 :
validate(self, attrs), attrs表示所有字段的字段名和字段值的大字典,可以从中获取多个字段的值进行校验,通过返回attrs;
校验不通过, raise serializers.ValidationError({'re_password': '两次密码不一致'})
4)如果需要提供数据的增加功能,需要重写create方法(它的返回值其实也是save()方法的返回值),完成数据的入库
create第二个参数是validated_data,打散之后添加到数据库,添加失败抛数据库异常
注:该字典包括校验通过的字段(去除了验证密码)以及有默认值的字段
validated_data = dict( list(self.validated_data.items()) + list(kwargs.items()) )
序列化类代码:
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('数据库入库失败') # 数据库异常在异常模块中完善
反序列化视图类:
UserDeserializer(data=反序列化数据, many=False)
1)反序列化数据可以为单个数据字典,many=False或者不写
2)反序列化数据可以为存放数据字典的单列集合,many=True必须明确(后面讲)
3)反序列化数据必须赋值给data关键字参数,才能进行数据校验
4)序列化对象.is_valid(raise_exception=True)校验失败会自动返回错误信息给前台
5)if 序列化对象.is_valid(): 可以自定义校验成功与失败分支的返回结果
成功可以完成新增或修改,失败错误信息在 序列化对象.errors 中
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)
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()), ]
2.序列化类
1)参与序列化与反序列化的字段(包括自定义字段)都必须明确
2)通过 read_only=True 表明该字段只参与序列化
3)通过 write_only=True 表明该字段只参与反序列化
4)参与反序列化,但是是选填字段(前台可以提供或不提供),用 required=False 处理
5)要完成数据库增加,需要重写 create 方法
6)要完成数据库更新,需要重写 update 方法,它有三个参数,instance是原数据,validated_data是要更新的数据
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()
视图类:
(1) 局部数据修改,设置 partial=True
(2)视图类给序列化类传递自定义参数 context=值 值一般用字典
在序列化类的钩子函数中用self.context获取传入的值
(3)对于单删,是把未删除对象中的is_delete=False改为True即可
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内部自定义的返回值决定 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, '删除成功')