–
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 今日作业
–
–
–
评论前必须登录!
注册