二 rest-framework序列化之Serializer
三 rest-framework序列化之ModelSerializer
序言:Django自带序列化组件
也就是使用原生的django来处理请求
在正式开始介绍Rest-Framework之前我们需要对它有一定的了解:
一、restframework介绍
什么是restframework
django restframework是基于django和restful协议开发的框架,在restful协议里,一切皆是资源,操作是通过请求方式控制,可以将python中的对象转换成json格式的字符串
restframework介绍
在开发REST API的视图中,虽然每个视图具体操作的数据不同,但增、删、改、查的实现流程基本套路化,所以这部分代码也是可以复用简化编写的:
增:校验请求数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回
删:判断要删除的数据是否存在 -> 执行数据库删除
改:判断要修改的数据是否存在 -> 校验请求的数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回
查:查询数据库 -> 将数据序列化并返回
Django REST framework可以帮助我们简化上述两部分的代码编写,大大提高REST API的开发速度。
HTTP动词
对于资源的具体操作类型,由HTTP动词表示。
常用的HTTP动词有下面四个(括号里是对应的SQL命令)。
GET(SELECT):从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
DELETE(DELETE):从服务器删除资源。
还有三个不常用的HTTP动词。
PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)。
HEAD:获取资源的元数据。
OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。
状态码
200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务) 204 NO CONTENT - [DELETE]:用户删除数据成功。 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功
更多状态码看这里:更多的状态码
下面我们主要介绍使用CBV在视图中处理请求的方式来,
在这里我们还是简单说一下,为什么使用CBV而不采用FBV:
之前介绍Django的时候我们使用的基本上都是FBV, 虽然函数简单明了,但如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。所以Django在后来加入了CBV。可以让我们用类写View,然后通过反射执行as_view()方法,这样做的优点主要下面两种:
提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性
下面我就正式开始介绍使用rest-framework在Django中处理请求,
注意:
1.记得使用之前没有安装Djangorestframework模块的要安装好该模块
2.安装成功之后一定要在setting中将rest_framework注册之后才能正常使用
二 rest-framework序列化之Serializer
序列化的意义:
web有两种应用模式,一种是前后端不分离,一种是前后端分离,当前后端分离的时候,后端只需要向前端传输数据即可,不需要进行其他的操作,而restframework在前后端传输数据时,主要是json数据,过程中就要需要把其他数据转换成json数据,比如数据库查询所有数据时,是queryset对象,那就要把这对象处理成json数据返回前端,一般如果是中大型公司,都是前后端分离,这也是目前的市场规则需要.
然后我们来看序列化时常用的字段和通用参数:
常用字段类型:
字段 | 字段构造方式和参数默认值 |
BooleanField | BooleanField() |
NullBooleanField | NullBooleanField |
CharField | CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True) |
EmailField | EmailField(max_length=None, min_length=None, allow_blank=False) |
SlugField | SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+ |
URLField | URLField(max_length=200, min_length=None, allow_blank=False) |
IntegerField | IntegerField(max_value=None, min_value=None) |
FloatField | FloatField(max_value=None, min_value=None) |
DecimalField | DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置 |
DateTimeField | DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None) |
DateField | DateField(format=api_settings.DATE_FORMAT, input_formats=None) |
TimeField | TimeField(format=api_settings.TIME_FORMAT, input_formats=None) |
DurationField | DurationField() |
ChoiceField | ChoiceField(choices) choices与Django的用法相同 |
FileField | FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ImageField | ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ListField | ListField(child=, min_length=None, max_length=None) |
DictField | DictField(child=) |
相对而言,常用字段类型是比较常见的,在我们的ORM模式里,只要是连接数据库那么就一定需要定义我们的模型参数,下面介绍静态常用的模型参数
通用参数
参数名称 | 说明 |
max_length | 最大长度 |
min_lenght | 最小长度 |
read_only | 表明该字段仅用于序列化输出,默认False |
write_only | 表明该字段仅用于反序列化输入,默认False |
required | 表明该字段在反序列化时必须输入,默认True |
default | 反序列化时使用的默认值 |
allow_null | 表明该字段是否允许传入None,默认False |
validators | 该字段使用的验证器 |
error_messages | 包含错误编号与错误信息的字典 |
label | 用于HTML展示API页面时,显示的字段名称 |
help_text | 用于HTML展示API页面时,显示的字段帮助提示信息 |
这里比较常用的字段是前面六个,其中max_length和min_length一般配合着charfield使用,可以给该字段设置大小上下限。而read_only和write_only是两个相反的概念,前者是不接收客户端的数据,只向客户端输出数据,后者是只接收客户端的数据,不向客户端输出数据,这就可以类比于我们登录注册时的密码框,我们只需要向它写入而并不需要它像我们输出,并且该字段是经过hash加密的,寻常情况难以解密
序列化实例
Models部分:
首先在应用的models中建立原始的数据库模型
from django.db import models # Create your models here. class Book(models.Model): title=models.CharField(max_length=32) price=models.IntegerField() pub_date=models.DateField() publish=models.ForeignKey("Publish") authors=models.ManyToManyField("Author") def __str__(self): return self.title class Publish(models.Model): name=models.CharField(max_length=32) email=models.EmailField() def __str__(self): return self.name class Author(models.Model): name=models.CharField(max_length=32) age=models.IntegerField() def __str__(self): return self.name
view部分:
在对数据库中取出来的数据进行序列化的时候的步骤:
-
需要写一个类来继承serializers.Serializer, 这个类可以单独写一个文件(建议), 也可以直接在view中书写,
-
书写的这个类中序列化的字段要和数据库中字段要保持高度一致
不一致的时候我们就要介绍一下在序列化的时候使用到的一个参数source:
source可以在后面指定要序列化的字段, 然后再序列化的类中字段就可以自己命名了
1 变量名和source指定的值不能一样
2 source='publish.name'还支持继续使用点语法
3 source 还支持方法(没啥用)
4 支持写方法对字段进行序列化, 如下
方法的返回值,会赋给前面定义的序列化的字段名字
方法一定传一个参数,是当前序列化的表对象
写方法的时候时固定写法,get_字段名(self, obj):pass
publish_dic=serializers.SerializerMethodField()
def get_publish_dic(self,obj):
这个obj是当前book对象.
return {'id':obj.publish.pk,'name':obj.publish.name}
from rest_framework.views import APIView from rest_framework.response import Response from .models import * from django.shortcuts import HttpResponse from django.core import serializers from rest_framework import serializers # 为序列化做准备 class BookSerializers(serializers.Serializer): title=serializers.CharField(max_length=32) price=serializers.IntegerField() pub_date=serializers.DateField() # 想要publish字段显示出版社名字的时候 publish=serializers.CharField(source="publish.name") #authors=serializers.CharField(source="authors.all") authors=serializers.SerializerMethodField() def get_authors(self,obj): temp=[] for author in obj.authors.all(): temp.append(author.name) return temp #此处可以继续用author的Serializers, # def get_authors(self,obj): # ret=obj.authors.all() # ss=AuthorSerializer(ret,many=True) # return ss.data # 序列化 class BookViewSet(APIView): def get(self,request,*args,**kwargs): book_list=Book.objects.all() # 序列化方式1: # 单个数据对象 model_to_dict(obj),是Django中的一个方法:返回一个字典,key是obj 这个 # 对象的字段名,value是字段对应的值。这种是最快的一种序列化的方式。 # from django.forms.models import model_to_dict # import json # data=[] # for obj in book_list: # data.append(model_to_dict(obj)) # print(data) # return HttpResponse("ok") # 序列化方式2: # data=serializers.serialize("json",book_list) # return HttpResponse(data) # 序列化方式3: bs=BookSerializers(book_list,many=True) #many=True代表有多条数据,如果只有一条 数据,many=False return Response(bs.data) # 序列化方式4: # ret=models.Book.objects.all().values('nid','title') # dd=list(ret) # return HttpResponse(json.dumps(dd))
注意:
1. source 如果是字段,会显示字段,如果是方法,会执行方法,不用加括号(authors=serializers.CharField(source='authors.all'))
2. rest-framework中对request和response都做了处理, 我们来看一下request在rest-framework中的请求流程及相关处理:
rest-framework中的request对象不再是Django中原生的HttpRequest对象, 而是rest-framework提供的扩展了HttpRequest类的Request类的对象
REST framework 提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析为类字典对象保存到Request对象中。
它和django大致相同,因为它的APIView继承是django的View,但在APiView中重写了dispatch方法
看到这段代码:
url(r'^publishers/$', views.PublishViewSet.as_view(),name="publish_list"),
执行PublishViewSet就是APIView的as_view方法
class APIView(View):
APIView继承了View,APIView中有as_view方法,所以会执行这个方法,方法中有这么一句代码
view = super(APIView, cls).as_view(**initkwargs)
最终还是执行了父类里的as_view方法,所以最终执行结果,得到这么这个view函数。
下面我们就去源码中看看这个函数的执行顺序:
def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs)
当请求来时,会执行view函数,然后结果调用了dispatch方法,而这里dispatch方法则不是View里的,因为APIView中重写了父类中的dispatch方法,并且是整个rest_framework中最重要的部分,实现了大部分逻辑。
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
所以我们可以总结,dispatch是通过view的函数的执行而被调用,那么它返回的结果就是view函数返回的结果,而view函数返回的结果就是as_view()方法返回的结果。也就是通过这样的方式获取到了请求方式并执行。
3. 使用rest-framework的Response的时候, 服务器端会根据请求的不同请求客户端而发送给客户端不同的数据, 比如使用浏览器访问的时候,会返回一个完整的带数据和格式的页面, 而使用postman进行访问的时候,只返回一个json格式的数据, 当然这两种方式返回的主要数据-json格式的数据都是一样的,只是一个带页面一个不带页面而已
如在模型中定义一个方法,直接可以在source指定执行
class UserInfo(models.Model): user_type_choices = ( (1,'普通用户'), (2,'VIP'), (3,'SVIP'), ) user_type = models.IntegerField(choices=user_type_choices) username = models.CharField(max_length=32,unique=True) password = models.CharField(max_length=64) #视图 ret=models.UserInfo.objects.filter(pk=1).first() aa=ret.get_user_type_display() #serializer xx=serializers.CharField(source='get_user_type_display')
序列化器的字段校验功能
# 三种方式 -字段自己的校验规则(max_length...) -validators的校验 publish = serializers.CharField(max_length=32,validators=[check,]) def check(data): if len(data)>10: raise ValidationError('最长不能超过10') else: return data -局部和全局钩子 # 局部钩子,validate_字段名,需要带一个data,data就是该字段的数据 def validate_title(self, data): if data.startswith('sb'): raise ValidationError('不能以sb开头') else: return data # 全局钩子 def validate(self, attrs): title=attrs.get('title') publish=attrs.get('publish') if title==publish: raise ValidationError('书名不能跟出版社同名') else: return attrs
read_only 和 write_only
read_only 表明该字段仅用于序列化输出,默认False write_only 表明该字段仅用于反序列化输入,默认False class BookSerializer(serializers.Serializer): # 要序列化哪个字段 id = serializers.IntegerField(required=False) # id=serializers.CharField() title = serializers.CharField(max_length=32,min_length=2,read_only=True) price = serializers.DecimalField(max_digits=5, decimal_places=2) # 序列化的时候看不到 publish = serializers.CharField(max_length=32,validators=[check,],write_only=True)
修改,删除接口
view
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def put(self, request, id): # 通过id取到对象 res = {'code': 100, 'msg': ''} try: book = models.Book.objects.get(id=id) ser = BookSerializer(instance=book, data=request.data) ser.is_valid(raise_exception=True) ser.save() res['msg'] = '修改成功' res['result'] = ser.data except Exception as e: res['code'] = 101 res['msg'] = str(e) return Response(res) def delete(self,request,id): response = {'code': 100, 'msg': '删除成功'} models.Book.objects.filter(id=id).delete() return Response(response)
serializer.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class BookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) title = serializers.CharField(max_length=32,min_length=2) price = serializers.DecimalField(max_digits=5, decimal_places=2) publish = serializers.CharField(max_length=32) def create(self, validated_data): res=models.Book.objects.create(**validated_data) print(res) return res def update(self, book, validated_data): book.title=validated_data.get('title') book.price=validated_data.get('price') book.publish=validated_data.get('publish') book.save() return book
高级用法之source
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 修改返回到前端的字段名 # source=title 字段名就不能再叫title name = serializers.CharField(max_length=32,min_length=2,source='title') 2 如果表模型中有方法 # 执行表模型中的test方法,并且把返回值赋值给xxx xxx=serializers.CharField(source='test') 3 sourc支持跨表操作 addr=serializers.CharField(source='publish.addr') # 希望你们去看以下源码,内部如何实现的
三 rest-framework序列化之ModelSerializer
-
使用modelSerializer的时候一定要指定序列化数据库中的哪张表
-
使用rest-framework来操作数据库的时候, 对数据进行增、删、改的时候一定要使用modelSerializer序列化过的
-
fields = "__all__": 该表的所有字段都进行序列化
-
fields=['nid', 'name', 'authors'] : 只序列化列表中的字段
-
exclude=('nid') : 除了元组里边的字段之外该表剩下所有字段进行序列化
-
在modelSerializer同样可以使用SerializerMethodField
-
在modelSerializer中可以定义局部钩子和全局钩子对某个字段进行二次校验, 或者加逻辑处理
-
使用钩子的时候也是固定写法, 局部钩子(def validate_字段名(self, value)) | 全局钩子(def validate(self, value))
-
在这里要注意加局部钩子的时候的代码缩进, 不要将钩子写到Meta中,写进去之后将无法执行钩子
-
使用局部钩子的时候,注意要传入value参数, 该参数的值是局部钩子要校验的字段的值
-
class BookSerializers(serializers.ModelSerializer): class Meta: model = models.Book # fields = "__all__" fields=['nid','title','authors','publish'] # exclude=('nid',) #不能跟fields同时用 # depth = 1 #深度控制,写 几 往里拿几层,层数越多,响应越慢,官方建议0--10之间,个人建议最多3层 publish=serializers.SerializerMethodField() def get_publish(self,obj): return obj.publish.name authors=serializers.SerializerMethodField() def get_authors(self,obj): ret=obj.authors.all() ss=AuthorSerializer(ret,many=True) return ss.data
解释:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 原来用的Serilizer跟表模型没有直接联系, 模型类序列化器ModelSerilizer,跟表模型有对应关系 2 使用 class BookModelSerializer(serializers.ModelSerializer): class Meta: model=表模型 # 跟哪个表模型建立关系 fields=[字段,字段] # 序列化的字段,反序列化的字段 fields='__all__' # 所有字段都序列化,反序列化 exclude=[字段,字段] # 排除哪些字段(不能跟fields同时使用) read_only_fields=['price','publish'] # 序列化显示的字段 write_only_fields=['title'] # 反序列化需要传入的字段 extra_kwargs ={'title':{'max_length':32,'write_only':True}} depth=1 # 了解,跨表1查询,最多建议写3 # 重写某些字段 publish = serializers.CharField(max_length=32,source='publish.name') # 局部钩子,全局钩子,跟原来完全一样 3 新增,修改 -统统不用重写create和update方法了,在ModelSerializer中重写了create和update
高级用法之SerializerMethodField
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class BookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) name = serializers.CharField(max_length=32,min_length=2,source='title') price = serializers.DecimalField(max_digits=5, decimal_places=2) publish = serializers.SerializerMethodField() def get_publish(self,obj): dic={'name':obj.publish.name,'addr':obj.publish.addr} return dic class BookModelSerializer(serializers.ModelSerializer): class Meta: model = models.Book fields = '__all__' publish = serializers.SerializerMethodField() def get_publish(self,obj): dic={'name':obj.publish.name,'addr':obj.publish.addr} return dic ## 第三中方案,使用序列化类的嵌套 class PublishSerializer(serializers.ModelSerializer): class Meta: model = models.Publish # fields = '__all__' fields = ['name','addr'] class BookModelSerializer(serializers.ModelSerializer): publish = PublishSerializer() class Meta: model = models.Book fields = '__all__'
四 生成hypermedialink(极少数)
class BookSerializers(serializers.ModelSerializer): class Meta: model = models.Book fields = "__all__" # 生成连接,直接查看出版社详情 publish = serializers.HyperlinkedIdentityField(view_name='ttt', lookup_field='publish_id', lookup_url_kwarg='pkk') authors=serializers.SerializerMethodField() def get_authors(self,obj): ret=obj.authors.all() ss=AuthorSerializer(ret,many=True) return ss.data #-------------- res=BookSerializers(ret,many=True,context={'request': request}) #-------------- class Publish(APIView): def get(self,request,pkk): print(pkk) return HttpResponse('ok') #----路由--- url(r'^publish/(?P<pkk>d+)$', views.Publish.as_view(),name='ttt'),
五 序列化组件之请求数据校验和保存功能
class BookSerializer1(serializers.Serializer): title=serializers.CharField(error_messages={'required': '标题不能为空'}) #这种方式要保存,必须重写create方法
通过源码查看留的校验字段的钩子函数:
#is_valid---->self.run_validation-(执行Serializer的run_validation)-->self.to_internal_value(data)---(执行Serializer的run_validation:485行) # 局部钩子 def validate_title(self, value): from rest_framework import exceptions raise exceptions.ValidationError('看你不顺眼') return value # 全局钩子 def validate(self, attrs): from rest_framework import exceptions if attrs.get('title')== attrs.get('title2'): return attrs else: raise exceptions.ValidationError('不想等啊')
序列化组件源码分析
''' 序列化组件,先调用__new__方法,如果many=True,生成ListSerializer对象,如果为False,生成Serializer对象 序列化对象.data方法--调用父类data方法---调用对象自己的to_representation(自定义的序列化类无此方法,去父类找) Aerializer类里有to_representation方法,for循环执行attribute = field.get_attribute(instance) 再去Field类里去找get_attribute方法,self.source_attrs就是被切分的source,然后执行get_attribute方法,source_attrs 当参数传过去,判断是方法就加括号执行,是属性就把值取出来 '''
many = True源码分析,局部全局钩子源码解析
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 many=True -__init__----->一路找到了BaseSerializer---》__new__决定了生成的对象是谁 2 入口是is_valid()---》BaseSerializer--》is_valid---》self._validated_data = self.run_validation(self.initial_data) -Serializer这个类的:self.run_validation def run_validation(self, data=empty): value = self.to_internal_value(data) # 局部字段自己的校验和局部钩子校验 try: self.run_validators(value) value = self.validate(value) # 全局钩子的校验 except (ValidationError, DjangoValidationError) as exc: raise ValidationError(detail=as_serializer_error(exc)) return value
图书的增删查改resful接口案例:
视图层:
class BookSerializers(serializers.ModelSerializer): class Meta: model=models.Book fields='__all__' class BookView(APIView): def get(self, request): book_list = models.Book.objects.all() bs = BookSerializers(book_list, many=True) # 序列化数据 return Response(bs.data) def post(self, request): # 添加一条数据 print(request.data) bs=BookSerializers(data=request.data) if bs.is_valid(): bs.save() # 生成记录 return Response(bs.data) else: return Response(bs.errors) class BookDetailView(APIView): def get(self,request,pk): book_obj=models.Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj,many=False) return Response(bs.data) def put(self,request,pk): book_obj = models.Book.objects.filter(pk=pk).first() bs=BookSerializers(data=request.data,instance=book_obj) if bs.is_valid(): bs.save() # update return Response(bs.data) else: return Response(bs.errors) def delete(self,request,pk): models.Book.objects.filter(pk=pk).delete() return Response("") 路由: url(r'^books/$', views.BookView.as_view()), url(r'^books/(?P<pk>d+)$', views.BookDetailView.as_view()),
drf的请求与响应
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# Request -data :前端以post请求提交的数据都在它中 -FILES :前端提交的文件 -query_params:就是原来的request.GET -重写了 __getattr__ -使用新的request.method其实取得就是原生request.method(通过反射实现) # Response -from rest_framework.response import Response -data:响应的字典 -status:http响应的状态码 -drf提供给你了所有的状态码,以及它的意思 from rest_framework.status import HTTP_201_CREATED -template_name:模板名字(一般不动),了解 -headers:响应头,字典 -content_type:响应的编码方式,了解 # 自己封装一个Response对象 class CommonResponse: def __init__(self): self.code=100 self.msg='' @property def get_dic(self): return self.__dict__ # 自己封装一个response,继承drf的Response # 通过配置,选择默认模板的显示形式(浏览器方式,json方式) -配置文件方式(全局) -如果没有配置,默认有浏览器和json -drf有默认配置文件 from rest_framework.settings import DEFAULTS REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类 'rest_framework.renderers.JSONRenderer', # json渲染器 'rest_framework.renderers.BrowsableAPIRenderer', # 浏览API渲染器 ) } -在视图类中配置(局部) -粒度更小 -class BookDetail(APIView): renderer_classes=[JSONRenderer,]
https://www.cnblogs.com/liupengfei1123/p/14956539.html