路漫漫其修远兮
吾将上下而求索

drf学习:版本、解析器、序列化

P30day130-01 课前分享

P31day130-02 今日内容概要

P32day130-03 内容回顾

P33day130-04 rest framework框架之版本使用

P34day130-05 rest framework框架之版本源码

P35day130-06 rest framework框架之解析器前戏

P36day130-07 rest framework框架之解析器流程分析

P37day130-08 rest framework框架之解析器源码

P38day130-09 rest framework框架之序列化基本使用

P39day130-10 rest framework框架之序列化自定义字段

P40day130-11 rest framework框架之序列化ModelSerializer

P41day130-12 rest framework框架之序列化小总结

P42day130-13 rest framework框架之序列化深度控制

P43day130-14 rest framework框架之序列化生成hypermedialink

P44day130-15 rest framework框架之序列化源码

P45day130-16 rest framework框架之序列化验证用户请求数据

P46day130-17 rest framework框架之序列化自定义验证规则

P47day130-18 今日作业

P30day130-01 课前分享
P31day130-02 今日内容概要
主要内容有:
    1、版本,重要程度:*
    2、解析器,重要程度:*
    3、queryset序列化,重要程度:****
    4、分页,重要程度:**
    5、路由,重要程度:**
    6、视图,重要程度:**
    7、渲染器,重要程度:*




P32day130-03 内容回顾
内容回顾




P33day130-04 rest framework框架之版本使用

当在执行:python manage.py makemigrations的时候没有反应,没有生成改动
可以指定特定的app来执行:python manage.py makemigrations app01

第一种是放到参数里面
http://127.0.0.1:8000/api/users?version=v1

    手动模拟可以
    class UsersView(APIView):
        
        def get(self, request, *args, **kwargs):
            version = request._request.GET.get('version')
            print(version)
            #http://127.0.0.1:8000/api/users?version=v1  可以从参数里面拿到值
            version = request.query_params.get('version')
            print(version)
            return HttpResponse('user list')
            
            
    下面一种是使用重写内置方法来实现的
    class ParamVersion(object):
        def determine_version(self, request, *args, **kwargs):
            version = request.query_params.get('version')
            return version


    class UsersView(APIView):
        versioning_class = ParamVersion
        def get(self, request, *args, **kwargs):
            print(request.version)
            return HttpResponse('user list')

        
    还有更简单的,drf已经写好的方法,和上面自定义的写法是一样的
    from rest_framework.versioning import QueryParameterVersioning

    class UsersView(APIView):
        versioning_class = QueryParameterVersioning
        def get(self, request, *args, **kwargs):
            print(request.version)
            return HttpResponse('user list')


    还可以全局配置
    REST_FRAMEWORK = {
        "DEFAULT_VERSION": 'v1',  #默认的版本
        "ALLOWED_VERSIONS": ['v1', 'v2'],  #允许的版本
        "VERSION_PARAM": 'version'  #意思是:http://127.0.0.1:8000/api/users?version=v1  这里的版本的变量名叫什么
    }
            
            

在url的路径中传参
http://127.0.0.1:8000/api/v1/users

    from rest_framework.versioning import QueryParameterVersioning, URLPathVersioning

    class UsersView(APIView):
        versioning_class = URLPathVersioning
        def get(self, request, *args, **kwargs):
            print(request.version)
            return HttpResponse('user list')
                
    url配置参考下面,其中version变量为版本号,被放到了request.version变量中
    urlpatterns = [
        url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),
        url(r'^(?P<version>[v1|v2]+)/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail')
    ]           
                
                
    可以放到全局配置中,使用这种路径
    "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning"           
            

            

P34day130-05 rest framework框架之版本源码

初始化里有下面两句,从request里面取出版本赋给request.version
def initial(self, request, *args, **kwargs):
    version, scheme = self.determine_version(request, *args, **kwargs)
    request.version, request.versioning_scheme = version, scheme            

再往下一层找,
def determine_version(self, request, *args, **kwargs):
    如果用户在自定义的类里面有versioning_class,假如在自定义类里面使用自定义的类versioning_class = URLPathVersioning
    如果在全局配置里面有,使用全局的
    if self.versioning_class is None:
        return (None, None)
    这里的self.versioning_class()是将URLPathVersioning类实例化,然后执行这个对象里面的方法
    scheme = self.versioning_class()
    这里的determine_version方法为URLPathVersioning类的方法
    此方法位置:versioning.determine_version:76行
    实际为执行后面的:URLPathVersioning().determine_version(request, *args, **kwargs)
    return (scheme.determine_version(request, *args, **kwargs), scheme)         
            
反向生成url,url配置如下
urlpatterns = [
    url(r'^(?P<version>[v1|v2]+)/users/$', UsersView.as_view(), name='aaa')
]


class UsersView(APIView):
    versioning_class = URLPathVersioning
    def get(self, request, *args, **kwargs):
        r1 = request.versioning_scheme.reverse(viewname='aaa',request=request)
        print(r1)           
            
可以得到:http://127.0.0.1:8000/api/v1/users/            
            
            

            
P35day130-06 rest framework框架之解析器前戏         
            
解析器:
前戏:django中间件解析的时候会做判断:request.POST/ request.body如果要使用这个,必须要满足下面的条件
    1、请求头要求:如果请求头中:Content-Type: application/x-www-form-urlencoded,request.POST中才有值(去request.body中解析数据)         
    2、数据格式要求:
        name=alex&age=18&gender=man
    如下面的情况就满足上面的两个要求:
        a、form表单提交,
            <form method...>
                    input...
            </form>
        b、ajax请求
        
        
        
            
P36day130-07 rest framework框架之解析器流程分析           
解析器:对请求体数据进行解析,前端页面发送给后端,有可能是form表单形式,有可能是json格式,这里可以定义进行解析 

from rest_framework.parsers import JSONParser,FormParser
class ParserView(APIView):
    parser_classes = [JSONParser,FormParser]
    """
    JSONParse:表示只能解析application/json头,不然就报错
    FormParser:表示只能解析application/x-www-form-urlencoded头,不然就报错
    如果这个list里面有两个,则先判断第一个,如果第一个不匹配,使用第二个进行解析
    """
    def post(self, request, *args, **kwargs):
        print(request.data)
        return HttpResponse('user list')


curl -H 'Content-Type: application/json' -d '{"name":"andy","age":18}' 192.168.170.30:8000/api/parse/
curl -H 'Content-Type: application/x-www-form-urlencoded' -d 'name=andy&age=18' 192.168.170.30:8000/api/parse/
            
            
                

P37day130-08 rest framework框架之解析器源码         
request.data的data方法位置:request.py 184行
往里面找
def data(self):
    if not _hasattr(self, '_full_data'):
        self._load_data_and_files()
    return self._full_data          
        
往里面找
def _load_data_and_files(self):
    """
    Parses the request content into `self.data`.
    """
    if not _hasattr(self, '_data'):
        self._data, self._files = self._parse()
        if self._files:
            self._full_data = self._data.copy()
            self._full_data.update(self._files)
        else:
            self._full_data = self._data

        # copy files refs to the underlying request so that closable
        # objects are handled appropriately.
        self._request._files = self._files          
        
往里面找
def _parse(self):
    
    这个是用户发送给服务端的类型
    media_type = self.content_type      
            
看到4分钟




P38day130-09 rest framework框架之序列化基本使用

简单的序列化,两种
一种是使用json.dumps来实现
class RolesView(APIView):
    parser_classes = [JSONParser,FormParser]
    
    def get(self, request, *args, **kwargs):
        #方法1
        roles = models.Role.objects.all().values('id','title')
        roles = list(roles)
        ret = json.dumps(roles, ensure_ascii=False)
        return HttpResponse(ret)


另一种:使用serialize来实现
from rest_framework import serializers
class RolesSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField()

    
class RolesView(APIView):
    parser_classes = [JSONParser,FormParser]

    def get(self, request, *args, **kwargs):
        #方法2
        roles = models.Role.objects.all()
        ser = RolesSerializer(instance=roles,many=True)
        ret = json.dumps(ser.data, ensure_ascii=False)
        return HttpResponse(ret)




P39day130-10 rest framework框架之序列化自定义字段


如果变量可执行,自动加括号执行
如果变量不可执行,只打印该变量
下面会用到该知识
def func(arg):
    if callable(arg):
        print(arg())
    else:
        print(arg)


func(123)
func(lambda: 666)


下面的例子涉及到选择,外键,many to many等情况,需要好好理解
class UserInfoSerializer(serializers.Serializer):
    username = serializers.CharField()
    #当变量名和models中的字段名不同,要使用source来指定对应字段名
    pwd = serializers.CharField(source='password')
    user_type = serializers.CharField()
    #找对应user_type对应的名字,
    #如果变量可执行,自动加括号执行
    #如果变量不可执行,只打印该变量
    #下面会用到该知识
    user_type_name = serializers.CharField(source='get_user_type_display')
    #外键,下面只会打印group对象
    #gp = serializers.CharField(source='group')
    gp_id = serializers.CharField(source='group.id')
    gp_title = serializers.CharField(source='group.title')
    #many to many对应,通过source做不到很好的管理
    # rls = serializers.CharField(source='roles.all')
    #自定义显示
    rls = serializers.SerializerMethodField()

    #自定义函数必须以get开头,以字段名结尾,返回什么,自定义就显示什么
    # def get_rls(self,row):
    #     return ['aaa']
    #下面row.roles.all()会查找每行的所有roles,然后追加
    def get_rls(self,row):
        role_obj_list = row.roles.all()
        ret = []
        for item in role_obj_list:
            ret.append({'id':item.id,'title':item.title})
        return ret


class UserInfoView(APIView):
    def get(self, request, *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)


涉及到示例数据
INSERT INTO `app01_role` VALUES ('1', '医生');
INSERT INTO `app01_role` VALUES ('2', '学生');
INSERT INTO `app01_role` VALUES ('3', '老师');

INSERT INTO `app01_usergroup` VALUES ('1', 'A组');

INSERT INTO `app01_userinfo` VALUES ('1', '1', 'andy', '123', '1');
INSERT INTO `app01_userinfo` VALUES ('2', '2', 'bob', '234', '1');

INSERT INTO `app01_userinfo_roles` VALUES ('1', '1', '1');
INSERT INTO `app01_userinfo_roles` VALUES ('2', '1', '2');
INSERT INTO `app01_userinfo_roles` VALUES ('3', '1', '3');
INSERT INTO `app01_userinfo_roles` VALUES ('4', '2', '1');




P40day130-11 rest framework框架之序列化ModelSerializer

下面的方式和上面的效果是一样的
比上面更省事

class UserInfoSerializer(serializers.ModelSerializer):
    #对于复杂的类型,自己定义,然后加到下面的fields字段里面
    user_type_name = serializers.CharField(source='get_user_type_display')
    #这个字段调用了下面的方法
    rls = serializers.SerializerMethodField()
    class Meta:
        model = models.UserInfo
        #下面的表示显示所有的字段
        #fields = '__all__'
        #下面表示显示自定义字段
        fields = ['id','username','password','user_type_name','rls']

    def get_rls(self,row):
        role_obj_list = row.roles.all()
        ret = []
        for item in role_obj_list:
            ret.append({'id':item.id,'title':item.title})
        return ret


class UserInfoView(APIView):
    def get(self, request, *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)




P41day130-12 rest framework框架之序列化小总结
写类的时候可以继承两种:serializers.Serializer或者serializers.ModelSerializer




P42day130-13 rest framework框架之序列化深度控制


下面有更简单的一种方式,自动序列化连表操作
class UserInfoSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.UserInfo
        fields = '__all__'
        #depth = 0  表示只查userinfo里面的字段
        #depth = 1  表示查询userinfo里面的字段和外键关联的更深一层的查询

class UserInfoView(APIView):
    def get(self, request, *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)

        

        
P43day130-14 rest framework框架之序列化生成hypermedialink

下面的示例:http://127.0.0.1:8000/api/group/1
通过url传入某个id,来获取对应的数据

urlpatterns = [
    url(r'group/(?P<pk>\d+)/$', GroupView.as_view()),
]

class GroupSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserGroup
        fields = '__all__'


class GroupView(APIView):
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        obj = models.UserGroup.objects.filter(pk=pk).first()
        ser = GroupSerializer(instance=obj,many=False)
        ret = json.dumps(ser.data, ensure_ascii=False)
        return HttpResponse(ret)

        

反向序列化成url,下面的示例,访问http://127.0.0.1:8000/api/userinfo/,得到如下数据,group这里直接就是能正常访问的链接
{
    "id": 1,
    "group": "http://127.0.0.1:8000/api/group/1/",
    "user_type": 1,
    "username": "andy",
    "password": "123",
    "roles": [
        1,
        2,
        3
    ]
}

url文件
urlpatterns = [
    url(r'roles/$', RolesView.as_view()),
    url(r'userinfo/$', UserInfoView.as_view()),
    url(r'group/(?P<xxx>\d+)/$', GroupView.as_view(), name='gp'),
]

view文件如下
class UserInfoSerializer(serializers.ModelSerializer):
    group = serializers.HyperlinkedIdentityField(view_name='gp', lookup_field='group_id', lookup_url_kwarg='xxx')
    class Meta:
        model = models.UserInfo
        fields = '__all__'
        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})
        print(ser.data)
        ret = json.dumps(ser.data, ensure_ascii=False)
        return HttpResponse(ret)


class GroupSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserGroup
        fields = '__all__'


class GroupView(APIView):
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('xxx')
        obj = models.UserGroup.objects.filter(pk=pk).first()
        ser = GroupSerializer(instance=obj,many=False)
        ret = json.dumps(ser.data, ensure_ascii=False)
        return HttpResponse(ret)




P44day130-15 rest framework框架之序列化源码
头疼




P45day130-16 rest framework框架之序列化验证用户请求数据

下面的示例是对用户提交的数据进行验证,用户请求如下
curl -H 'Content-Type: application/json' -d '{"title":"老男人", "age":18}' 192.168.170.30:8000/api/usergroup/
用户提交的数据,可以自定义验证规则类,设置title必须是以"老男人"开头,否则报设定的错误信息

class XXXValidator(object):
    def __init__(self, base):
        #这里的base = '老男人'
        self.base = base

    def __call__(self, value):
        #这里的value就是提交过来的值
        if not value.startswith(self.base):
            message = 'mast start with %s' % self.base
            raise serializers.ValidationError(message)


class UserGroupSerializer(serializers.Serializer):
    title = serializers.CharField(error_messages={'required':'标题不能为空'}, validators=[XXXValidator('老男人')])


class UserGroupView(APIView):
    def post(self, request, *args, **kwargs):
        ser = UserGroupSerializer(data=request.data)
        if ser.is_valid():
            print(ser.validated_data['title'])
        else:
            print(ser.errors)
        return HttpResponse('hello')




P46day130-17 rest framework框架之序列化自定义验证规则
总结
P47day130-18 今日作业

未经允许不得转载:江哥架构师笔记 » drf学习:版本、解析器、序列化

分享到:更多 ()

评论 抢沙发

评论前必须登录!