博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python之路_rest-framework之版本、解析器、序列化
阅读量:4661 次
发布时间:2019-06-09

本文共 14091 字,大约阅读时间需要 46 分钟。

一、版本控制

 

1、基于url传参

  如通过这样的url传参方式实现版本控制:http://127.0.0.1:8080/api/users/?version=v2,其中参数名称version和允许的版本通过如下配置文件方式实现,不再允许的版本内的版本是无法通过访问的。

配置文件内容:

REST_FRAMEWORK = {    'DEFAULT_VERSION': 'v1',                  #默认的版本    'ALLOWED_VERSIONS': ['v1','v2'],          #允许的版本    'VERSION_PARAM': 'version',               #url中获取值的可以    }

具体使用实例:

  url代码:

from django.conf.urls import url, includefrom web.views import TestViewurlpatterns = [    url(r'^test/', TestView.as_view(),name='test'),]

  视图代码:

from rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework.versioning import QueryParameterVersioningclass TestView(APIView):    versioning_class = QueryParameterVersioning    def get(self, request, *args, **kwargs):          print(request.version)                                                    # 获取版本        print(request.versioning_scheme)                                          # 获取版本管理的类        if request.version == "v1":            data="这是版本一"        elif request.version == "v2":            data= "这是版本二"        else:            data="版本错误"        reverse_url = request.versioning_scheme.reverse('test', request=request)  # 反向生成URL        print(reverse_url)        return Response(data)

2、基于url正则

  如通过这样的url正则方式实现版本控制:http://127.0.0.1:8080/api/v1/users,其中配置方式与上述一致。具体实例如下:

配置文件:

REST_FRAMEWORK = {    'DEFAULT_VERSION': 'v1',            # 默认版本    'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允许的版本    'VERSION_PARAM': 'version'          # URL中获取值的key}

url代码:

from django.conf.urls import url, includefrom app01.views import TestViewurlpatterns = [    url(r'^(?P
[v1|v2]+)/test/', TestView.as_view(), name='test'),

视图代码:

from rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework.versioning import URLPathVersioningclass TestView(APIView):    versioning_class = URLPathVersioning    def get(self, request, *args, **kwargs):               print(request.version)                                                      #获取版本        print(request.versioning_scheme)                                            #获取版本管理的类        if request.version == "v1":            data="这是版本一"        elif request.version == "v2":            data= "这是版本二"        else:            data="版本错误"        reverse_url = request.versioning_scheme.reverse('test', request=request)    #反向生成URL        print(reverse_url)        return Response(data)

3、基于主机名

  如:v1.example.com,主要实现实例如下,注意代码在进行本地测试的时候需要在电脑的host文件中配置主机名对应的主机名,如127.0.0.1   v1.example.com

配置代码:

ALLOWED_HOSTS = ['*']                      #允许所有的主机名REST_FRAMEWORK = {    'DEFAULT_VERSION': 'v1',               # 默认版本    'ALLOWED_VERSIONS': ['v1', 'v2'],      # 允许的版本    'VERSION_PARAM': 'version'             # URL中获取值的key}

url代码:

from django.conf.urls import url, includefrom web.views import TestViewurlpatterns = [    url(r'^test/', TestView.as_view(), name='test'),]

视图代码:

from rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework.versioning import HostNameVersioningclass TestView(APIView):    versioning_class = HostNameVersioning    def get(self, request, *args, **kwargs):               print(request.version)                                                      #获取版本        print(request.versioning_scheme)                                            #获取版本管理的类        if request.version == "v1":            data="这是版本一"        elif request.version == "v2":            data= "这是版本二"        else:            data="版本错误"        reverse_url = request.versioning_scheme.reverse('test', request=request)    #反向生成URL        print(reverse_url)        return Response(data)

4、全局使用

  以上实例中均是在局部视图使用的情况,如果想在全局使用,只需要按照如下方式进行配置,局部视图不用再做任何的配置即可使用。具体的配置方式如下:

REST_FRAMEWORK = {    'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning",      #使用哪一种,配置对应的版本类    'DEFAULT_VERSION': 'v1',    'ALLOWED_VERSIONS': ['v1', 'v2'],    'VERSION_PARAM': 'version' }

二、解析器

  对请求体中的数据进行解析的作用。只有在执行request.data时才出发解析器。具体应用实例如下:

1、仅处理请求头content-type为application/json的请求体

from rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework.request import Requestfrom rest_framework.parsers import JSONParserclass TestView(APIView):    parser_classes = [JSONParser, ]    def post(self, request, *args, **kwargs):        print(request.content_type)        # 获取请求的值,并使用对应的JSONParser进行处理        print(request.data)        # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值        print(request.POST)        print(request.FILES)        return Response('POST请求,响应内容')

2、仅处理请求头content-type为application/x-www-form-urlencoded 的请求体

from rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework.request import Requestfrom rest_framework.parsers import FormParserclass TestView(APIView):    parser_classes = [FormParser, ]    def post(self, request, *args, **kwargs):        print(request.content_type)        # 获取请求的值,并使用对应的JSONParser进行处理        print(request.data)        # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值        print(request.POST)        print(request.FILES)        return Response('POST请求,响应内容')

3、全局使用

  只需要在配置文件中做如下配置即可:

REST_FRAMEWORK = {    'DEFAULT_PARSER_CLASSES':[        'rest_framework.parsers.JSONParser'        'rest_framework.parsers.FormParser'        'rest_framework.parsers.MultiPartParser'    ]}

  总结:如果客户端的Content-Type的值和 application/json 匹配:JSONParser处理数据;如果客户端的Content-Type的值和 application/x-www-form-urlencoded 匹配:FormParser处理数据。

  注意:个别特殊的值可以通过Django的request对象 request._request 来进行获取,其他解析器请参看博客:

 

三、序列化

  对于序列化的概念,我们并不陌生。在restful中序列化是将查询的queryset对象序列化成有序字典的过程。具体方式介绍如下,实例中均以如下表结构为依据:

from django.db import modelsclass Menu(models.Model):    name = models.CharField(max_length=32)class Group(models.Model):    title = models.CharField(max_length=32)    mu = models.ForeignKey(to="Menu",default=1)class UserInfo(models.Model):    name = models.CharField(max_length=32)    pwd = models.CharField(max_length=32)    group = models.ForeignKey(to='Group')    roles = models.ManyToManyField(to='Role')class Role(models.Model):    name = models.CharField(max_length=32)

1、基于Serializer

a.基本操作

from rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework import serializersfrom . import modelsclass UsersSerializer(serializers.Serializer):    name = serializers.CharField()    pwd = serializers.CharField()class UsersView(APIView):    def get(self, request, *args, **kwargs):        # 方式一(未序列化):        user_list = models.UserInfo.objects.all().values('name','pwd','group__id',"group__title")        return Response(user_list)        # 方式二之多对象        user_list = models.UserInfo.objects.all()        ser = UsersSerializer(instance=user_list,many=True)                           #many=True        return Response(ser.data)        # 方式二之单对象        user = models.UserInfo.objects.all().first()        ser = UsersSerializer(instance=user, many=False)                              #many=False        return Response(ser.data)

b.简单跨表

class UsersSerializer(serializers.Serializer):    name = serializers.CharField()    pwd = serializers.CharField()    '''    跨表字段必须通过source给与指定,如下    group_name和menu_name可以随便定义。    '''    group_name = serializers.CharField(source="group.title")    menu_name = serializers.CharField(source="group.mu.name")class UsersView(APIView):    def get(self,request,*args,**kwargs):        user_list = models.UserInfo.objects.all()        ser = UsersSerializer(instance=user_list,many=True)        return Response(ser.data)

c.复杂跨表

  对于多对多表关系,想要查询并序列化可就没有多对一表关系查询name简单了。需要另外重构一个类处理多对多字段,具体实例如下:

1、方式一

#方式一:class MyCharField(serializers.CharField):    '''    用于查询多对多字段,to_representation方法用于处理查询数据,    本例为例,value接收source="roles.all"参数,可以自定义要查    询的多对多表中的数据及要显示的数据结构。    '''    def to_representation(self, value):        data_list = []        for row in value:            data_list.append(row.name)        return data_listclass UsersSerializer(serializers.Serializer):    name = serializers.CharField()       pwd = serializers.CharField()      group_name = serializers.CharField(source="group.title")     menu_name = serializers.CharField(source="group.mu.name")     x2 = MyCharField(source="roles.all")

 2、方式二

#方式二:class MyCharField(serializers.CharField):    def to_representation(self, value):        return {
'id':value.pk, 'name':value.name}class UsersSerializer(serializers.Serializer): name = serializers.CharField() pwd = serializers.CharField() group_id = serializers.CharField() group_name = serializers.CharField(source="group.title") menu_name = serializers.CharField(source="group.mu.name") x2 = serializers.ListField(child=MyCharField(),source="roles.all")

3、方式三(推荐)

class UsersSerializer(serializers.Serializer):    name = serializers.CharField()    pwd = serializers.CharField()    group_name = serializers.CharField(source="group.title")    menu_name = serializers.CharField(source="group.mu.name")    x2 = serializers.SerializerMethodField()    def get_x2(self,obj):    #get_x2中的x2必须与上述x2变量名保持一致        obj.roles.all()        role_list = obj.roles.filter(id__gt=1)        data_list = []        for row in role_list:            data_list.append({
'pk':row.pk,'name':row.name}) return data_list

  以上三种实现方式对应的视图相同,如下:

class UsersView(APIView):    def get(self,request,*args,**kwargs):        user_list = models.UserInfo.objects.all()        ser = UsersSerializer(instance=user_list,many=True)        return Response(ser.data)

2、基于ModelSerializer

a、基本使用

class UsersSerializer(serializers.ModelSerializer):    class Meta:        model = models.UserInfo        fields = "__all__"        # fields = ['name', 'pwd','group']        '''        可以设置0-10间数字,作用:可以让group字段跨表显示相应的组信息,        若设置2,则group表中关联的mu字段对应的表信息也会显示。       '''        depth = 1   class UsersView(APIView):    def get(self,request,*args,**kwargs):        user_list = models.UserInfo.objects.all()        ser = UsersSerializer(instance=user_list,many=True)        return Response(ser.data)

b.简单跨表

class UsersSerializer(serializers.ModelSerializer):    x1 = serializers.CharField(source='group.title')    class Meta:        model = models.UserInfo        fields = ['name', 'pwd','x1']   #把上述的x1添加到fields中        depth = 1class UsersView(APIView):    def get(self,request,*args,**kwargs):        user_list = models.UserInfo.objects.all()        ser = UsersSerializer(instance=user_list,many=True)        return Response(ser.data)

c.反向生成url

class UsersSerializer(serializers.ModelSerializer):    group = serializers.HyperlinkedIdentityField(view_name='detail')   #view_name为url别名    class Meta:        model = models.UserInfo        fields = ['name', 'pwd','group']                               #返回的group反向解析的url,group为随便起的变量        depth = 1class UsersView(APIView):    def get(self,request,*args,**kwargs):        user_list = models.UserInfo.objects.all()        ser = UsersSerializer(instance=user_list,many=True,context={
'request':request}) #必须有,context={'request':request} return Response(ser.data)

  原url为:url(r'^xxx/(?P<pk>\d+)', views.UsersView.as_view(),name='detail'),参数必须为pk,反向生成的url中pk对应的条信息的id。

3、基于HyperlinkedModelSerializer

  全局生成URL:

class UsersSerializer(serializers.HyperlinkedModelSerializer):    class Meta:        model = models.UserInfo        fields = "__all__"        # fields = ['id','name','pwd']class UsersView(APIView):    def get(self,request,*args,**kwargs):        user_list = models.UserInfo.objects.all()        ser = UsersSerializer(instance=user_list,many=True,context={
'request':request}) return Response(ser.data)

  其中原url为:

'''以下url别名必须为表名或者关联字段加detail组成,最终userinfo-detail别名会生成包含该条信息id的url,group-detail会生成包含对应组id的url。。。'''url(r'^xxx/(?P
\d+)', views.UsersView.as_view(), name='userinfo-detail'), url(r'^xxx/(?P
\d+)', views.UsersView.as_view(),name='group-detail'), url(r'^xxx/(?P
\d+)', views.UsersView.as_view(),name='role-detail'),

四、请求数据验证

1、场景一

class PasswordValidator(object):    def __init__(self, base):        self.base = base    def __call__(self, value):        if value != self.base:            message = '用户输入的值必须是 %s.' % self.base            raise serializers.ValidationError(message)    def set_context(self, serializer_field):        """        This hook is called by the serializer instance,        prior to the validation call being made.        """        # 执行验证之前调用,serializer_fields是当前字段对象        passclass UsersSerializer(serializers.Serializer):        name = serializers.CharField(min_length=6)        pwd = serializers.CharField(error_messages={
'required': '密码不能为空'}, validators=[PasswordValidator('666')]) #用于对post数据的校验 class UsersView(APIView): def post(self,request,*args,**kwargs): ser = UsersSerializer(data=request.data) if ser.is_valid(): print(ser.validated_data) else: print(ser.errors) return Response('...')

2、场景二

class PasswordValidator(object):    def __init__(self, base):        self.base = base    def __call__(self, value):        if value != self.base:            message = '用户输入的值必须是 %s.' % self.base            raise serializers.ValidationError(message)    def set_context(self, serializer_field):        """        This hook is called by the serializer instance,        prior to the validation call being made.        """        # 执行验证之前调用,serializer_fields是当前字段对象        passclass UsersSerializer(serializers.ModelSerializer):    class Meta:        model = models.UserInfo        fields = "__all__"        extra_kwargs = {            'name': {
'min_length': 6}, 'pwd': {
'validators': [PasswordValidator(666), ]} }#用于对post数据的校验class UsersView(APIView): def post(self,request,*args,**kwargs): ser = UsersSerializer(data=request.data) if ser.is_valid(): print(ser.validated_data) else: print(ser.errors) return Response('...')

 

转载于:https://www.cnblogs.com/seven-007/p/8470103.html

你可能感兴趣的文章