https://api.example.com/v1/
?limit=10:指定返回记录的数量 ?offset=10:指定返回记录的开始位置。 ?page=2&per_page=100:指定第几页,以及每页的记录数。 ?sortby=name&order=asc:指定返回结果按照哪一个属性排序,以及排序顺序。 ?animal_type_id=1:指定筛选条件
知乎连接html
Python的多重继承正如文档所言是深度优先从左至右不重复 在Python里,当你新构造一个对象时,有两个步骤:首先是自底向上,从左至右调用__new__,而后再依照递归栈依次调用__init__
单个视图使用: class 类中指定: versioning_class = QueryParameterVersioning 全局使用:python
settings中配置生效,全局使用 REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning", "DEFAULT_VERSION":'v1', "ALLOWED_VERSIONS":['v1','v2'], "VERSION_PARAM":'version', "DEFAULT_PARSER_CLASSES":['rest_framework.parsers.JSONParser','rest_framework.parsers.FormParser'] }
E:\space_env\env_for_blog\Lib\site-packages\rest_framework\versioning.py 一个基类,5个版本控制类:数据库
BaseVersioning QueryParameterVersioning(BaseVersioning) URLPathVersioning(BaseVersioning) AcceptHeaderVersioning(BaseVersioning) HostNameVersioning(BaseVersioning) NamespaceVersioning(BaseVersioning)django
BaseVersioning 中有三个函数 def determine_version 返回值是 version 子类覆写的钩子 def reverse 反向生成url,每一个子类生成的方式不同 def is_allowed_version 给后边子类提供可支持的版本号啊json
class URLPathVersioning(BaseVersioning): # 版本错误信息 invalid_version_message = _('Invalid version in URL path.') def determine_version() ··· return version def reverse(): return父类的reverse方法
探究后端
APIView -- dispatch -- self.initial(request, *args, **kwargs) # Determine the API version, if versioning is in use. version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme def determine_version(self, request, *args, **kwargs): """ 返回元组: (version, versioning_scheme) """ if self.versioning_class is None: return (None, None) scheme = self.versioning_class() return (scheme.determine_version(request, *args, **kwargs), scheme) self.initial传入的是新request 在APIView - def determine_version里边:获取了version版本, versioning_scheme版本类对象 而后把这俩值封装到request参数里边 # 获取版本 print(request.version) # 获取处理版本的对象 print(request.versioning_scheme)
reverse 利用restful - reverseapi
# 反向生成URL(rest framework) u1 = request.versioning_scheme.reverse(viewname='uuu',request=request) print(u1)
利用django自带的reverse服务器
# 反向生成URL u2 = reverse(viewname='uuu',kwargs={'version':2}) print(u2) 这个须要本身指定kwargs
对应的类是:versioning_class = QueryParameterVersioning http://127.0.0.1:8000/api/users/?version=v2restful
对应类:URLPathVersioning(BaseVersioning) url(r'^(?P<version>[v1|v2]+)/users/$', views.UsersView.as_view(),name='uuu'), http://127.0.0.1:8000/api/users/?version=v2网络
【注意】 配置或测试URL时,须要保证所输入的URL符合规则 也就是说,变量得是那个v1/v2 才能顺利匹配版本/返回invalid_version_message 若是连URL开头都没匹配上的话,那只能404了,压根走不到版本控制的代码处
后端要想成功的解析出数据,有两点要求: 1. 请求头要写对,好比 Content-Type: application/x-www-form-urlencoded 2. 数据格式要与请求头标识的一致
form提交时:Content-Type: application/x-www-form-urlencoded 后端:request.POST从中取值 application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值 其他状况,须要request.body取值
rest_framework 解析器,对请求体数据进行解析
BaseParser 基类 没卵用 JSONParser media_type = 'application/json' FormParser media_type = 'application/x-www-form-urlencoded' MultiPartParser media_type = 'multipart/form-data' Parser for multipart form data, which may include file data. 文件传输,也用这个解析 FileUploadParser media_type = '/' 这个貌似是上传数据用的,用到再看
class ParserView(APIView): parser_classes = [JSONParser,FormParser,] """ JSONParser:表示只能解析content-type:application/json头 JSONParser:表示只能解析content-type:application/x-www-form-urlencoded头 """ def post(self,request,*args,**kwargs): print(request.data) return HttpResponse('ParserView返回')
封装参数 dispatch - self.initialize_request - parsers=self.get_parsers() request.data Request() 类 >>> def data()方法 >>> _load_data_and_files is_form_media_type 直接copy 或者 self._parse() media_type = self.content_type - 获取content_type stream = self.stream - 调用Request/stream()获取body数据 parser = self.negotiator.select_parser(self, self.parsers) - 获取设置的序列化类
parsed = parser.parse(stream, media_type, self.parser_context) - 序列化 try: return (parsed.data, parsed.files) except AttributeError: empty_files = MultiValueDict() return (parsed, empty_files)
【概念】:序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化,流的概念这里不用多说(就是I/O), 咱们能够对流化后的对象进行读写操做,也可将流化后的对象传输于网络之间(注:要想将对象传输于网络必须进行流化)! 在对对象流进行读写操做时会引起一些问题,而序列化机制正是用来解决这些问题的!
序列化器容许将诸如查询集和模型实例之类的复杂数据转换为原生 Python 数据类型,而后能够将它们轻松地呈现为 JSON,XML 或其余内容类型。 序列化器还提供反序列化,在首次验证传入数据以后,能够将解析的数据转换回复杂类型(并作验证)。
REST framework 中的序列化类与 Django 的 Form 和 ModelForm 类很是类似。 咱们提供了一个 Serializer 类,它提供了一种强大的通用方法来控制响应的输出,以及一个 ModelSerializer 类, 它为建立处理模型实例和查询集的序列化提供了有效的快捷方式。
序列化在django-rest-framework中的典型应用就是:
从数据库中取数据,而后将其序列化为支持网络传输的表述(如json)
验证,对请求发来的数据进行验证.对发来的json串解析为Serializer对应的字段 ser.is_valid()验证,而后ser.validated_data取数据进行操做,或者ser.errors提取错误信息
咱们能够经过声明序列来完成,这些序列与Django的表单(forms)工做类似。 forms是DB(后端)与HTML的桥梁,serializers是DB(后端)与传输的转换器 二者在设计思路和使用方式上有不少雷同,源码流程也很类似
def get(self,request,*args,**kwargs): roles = models.Role.objects.all().values('id','title') roles = list(roles) ret = json.dumps(roles,ensure_ascii=False) return HttpResponse(ret)
【注意】ensure_ascii=False 表示不转码,就按照数据库原始数据形式来发
class RolesSerializer(serializers.Serializer): # 字段名与数据库名称一致 id = serializers.IntegerField() title = serializers.CharField() class RolesView(APIView): def get(self,request,*args,**kwargs): roles = models.Role.objects.all() ser = RolesSerializer(instance=roles,many=True) ret = json.dumps(ser.data, ensure_ascii=False) # 对于单个对象,many=False role = models.Role.objects.all().first() ser = RolesSerializer(instance=role, many=False) # ser.data 已是转换完成的结果 ret = json.dumps(ser.data, ensure_ascii=False) return HttpResponse(ret)
print(ser.data,type(ser.data)) {'title': '开发', 'id': 1} <class 'rest_framework.utils.serializer_helpers.ReturnDict'> ser.data 是字典(有序字典)
user_type = serializers.CharField() 等价于 xxxxx = serializers.CharField(source="user_type") 转换完的字段名 user_type 变为 xxxxx
因此能够这样写: user_type = serializers.CharField(source="user_type") 显示存储的代号 user_type = serializers.CharField(source="get_user_type_display") 显示对应的名称
外键关联时: gp = serializers.CharField(source="group.title") 内部能够根据 . 来split,一直向后取值
内部: 源码会判断传入的值是否callable,若是可调用,自动加()执行 source="user_type" >>> row.user_type source="get_user_type_display" >>> row.get_user_type_display()
def fun(arg): if callable(arg): ret = arg() else: ret = arg print(ret) def add(a,b): return a + b fun('测试') fun(add(2,6))
真正的自定义显示,钩子
rls = serializers.SerializerMethodField() # 自定义显示 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 # my_field = serializer.SerializerMethodField(method_name='get_my_field') # default_method_name = 'get_{field_name}'.format(field_name=field_name)
自定义filed
class MyField(serializers.CharField): def to_representation(self, value): print(value) return "真正的返回值" 使用: class UserInfoSerializer(serializers.Serializer): my_field = MyField(source='username') 返回时,显示的就是return内容
也能够支持自定义字段。
指定字段时能省点事,仅此而已
class UserInfoSerializer(serializers.ModelSerializer): oooo = serializers.CharField(source="get_user_type_display") rls = serializers.SerializerMethodField() # 自定义显示钩子 gp = serializers.CharField(source="group.title") def get_rls(self,row): return ({'name':'jinshen'}) class Meta: model = models.UserInfo # fields = "__all__" fields = ['id','username','password','group','roles'] class UserInfoView2(APIView): def get(self, request, *args, **kwargs): users = models.UserInfo.objects.all() ser = UserInfoSerializer2(instance=users, many=True, context={'request': request}) ret = json.dumps(ser.data, ensure_ascii=False) return HttpResponse(ret)
class UserInfoSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo # fields = "__all__" fields = ['id','username','password','group','roles'] depth = 1 # 0 ~ 10
serializers.HyperlinkedIdentityField
# group字段对应生成url连接 - "group": "http://127.0.0.1:8000/api/v1/group/1", class UserInfoSerializer3(serializers.ModelSerializer): group = serializers.HyperlinkedIdentityField(view_name='gp',lookup_field='group_id',lookup_url_kwarg='pk') ''' view_name='gp' url别名 lookup_field='group_id' 对应的字段列,默认pk lookup_url_kwarg='pk' url中的分组名,能够本身改 没有source参数! ''' class Meta: model = models.UserInfo fields = ['id','username','password','group','roles'] depth = 0 # 0 ~ 10 class UserInfoView3(APIView): def get(self, request, *args, **kwargs): users = models.UserInfo.objects.all() '''若是要生成连接,必须加上context参数''' ser = UserInfoSerializer3(instance=users, many=True, context={'request': request}) ret = json.dumps(ser.data, ensure_ascii=False) return HttpResponse(ret)
__new__ 比 __init__要早执行
__new__和__init__
__new__和__init__
kwargs.pop('many', False) 取many值,缺省默认False, def __new__(cls, *args, **kwargs): # We override this method in order to automagically create # `ListSerializer` classes instead when `many=True` is set. if kwargs.pop('many', False): # many = True时执行,对queryset进行处理 return cls.many_init(*args, **kwargs) # many缺省或为False时执行,对obj进行处理 return super(BaseSerializer, cls).__new__(cls, *args, **kwargs) def __init__(): pass 一坨赋值语句封装参数
分析
many = False
return super(BaseSerializer, cls).__new__(cls, *args, **kwargs)
返回的是本身,啥都没作。new就完事了 ,接下来执行init方法去了 many = True 执行 many_init() 方法 这个方法作了一件事 把处理序列化的类指定为 ListSerializer,并返回
小结 ser = RolesSerializer(instance=role, many=False) 类实例化作的事:作区分 对象, Serializer类处理; self.to_representation QuerySet,ListSerializer类处理; self.to_representation
Serializer - data方法
@property def data(self): ret = super(Serializer, self).data #调用父类data方法 return ReturnDict(ret, serializer=self)
BaseSerializer - data方法
if not hasattr(self, '_data'): if self.instance is not None and not getattr(self, '_errors', None): self._data = self.to_representation(self.instance) elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None): self._data = self.to_representation(self.validated_data) else: self._data = self.get_initial() return self._data
to_representation方法, 依然是从下往上找,Serializer和BaseSerializer里都有此方法 可是Serializer-to_representation 离得更近,它会执行
def to_representation(self, instance): """ Object instance -> Dict of primitive datatypes. """ ret = OrderedDict() #这就是最后返回给咱们的那个有序字典实例 fields = self._readable_fields for field in fields: try: attribute = field.get_attribute(instance) except SkipField: continue ···
field 是咱们在自定义序列化类时指定的 Charfiled IntegerField 这些字段对象 Charfiled 等,里边没有get_attribute() 方法 Charfiled继承自Filed class Filed() - get_attribute()
def get_attribute(self, instance): """ Given the *outgoing* object instance, return the primitive value that should be used for this field. """ try: return get_attribute(instance, self.source_attrs)
instance 是传入的对象 self.source_attrs就是参数source, 在类的init里能够看到它的封装 在Filed类下的bind()方法,能够看到split . 取值 source_attrs 能够是:group.title get_user_type_display roles.all等等 经过后边的一系列骚操做,无论它是什么,都能从里边拿出想要的东西
上来就跳了,返回单独的get_attribute()方法 把两个重要的参数传入
def get_attribute(instance, attrs): for attr in attrs: # 循环全部的attr(source参数) try: if isinstance(instance, collections.Mapping): instance = instance[attr] else: instance = getattr(instance, attr) except ObjectDoesNotExist: return None if is_simple_callable(instance): try: instance = instance() except (AttributeError, KeyError) as exc: # If we raised an Attribute or KeyError here it'd get treated # as an omitted field in `Field.get_attribute()`. Instead we # raise a ValueError to ensure the exception is not masked. raise ValueError('Exception raised in callable attribute "{0}"; original exception was: {1}'.format(attr, exc)) return instance
经过在这个函数里遍历,根据source取出各层的instance实例并逐层返回
serializers.py 文件在开头引入包的时候,就引入了fields.py的全部字段 example_field = serializers.CharField(...) 用的实际上是fileds里边的Charfiled class CharField(Field): 继承自 class Field(object): 咱们看看Field支持哪些参数:
class Field(object): default_error_messages = { 'required': _('This field is required.'), 'null': _('This field may not be null.') } default_validators = [] def __init__(self, read_only=False, write_only=False, required=None, default=empty, initial=empty, source=None, label=None, help_text=None, style=None, error_messages=None, validators=None, allow_null=False):
default_error_messages 两条默认的错误提示 分别对应的参数是:required和allow_null
validators参数对应的就是验证方法(基于类),默认空列表
class MultipleOf(object): def __init__(self, base): self.base = base def __call__(self, value): # __call__()方法可以让类的实例对象,像函数同样被调用; # XXValidator(12)(34) 第一个()实例化,第二个()内的34会被识别为参数 if value % self.base != 0: message = 'This field must be a multiple of %d.' % self.base raise serializers.ValidationError(message) 使用: title = serializers.CharField(error_messages={'required': '标题不能为空'}, validators=[MultipleOf('老男人'), ])
上边这种方法,实现了对单个字段的验证。哪一个须要验,就在哪写validators=[这个类]
【小总结】 自定义validator类,与form里边自定义validator类用法彻底一致 这个validator是filed参数,也就是只能做为参数对单个字段进行验证 具体调用的地方在···\rest_framework\fields.py - class Field() - def run_validators()
try: validator(value) except ValidationError as exc: pass
【小总结over】
另外,还能够经过自定义字段的方法实现验证。 django-form中 全部字段对象都继承自class Field ,在这个类里边留了一些钩子方法 如:to_python() validate() 自定义一个字段类,覆写以上方法
当调用表单的 is_valid() 方法时, is_valid()>>> self.errors>>>self.full_clean()>>>self._clean_fields()>>>value = field.clean(value) 调用自定义字段的clean()
def clean(self, value): """ Validates the given value and returns its "cleaned" value as an appropriate Python object. Raises ValidationError for any errors. """ value = self.to_python(value) # 字段里自定义to_python,返回value self.validate(value) # 字段里自定义validate,返回对象 self.run_validators(value) # 自定义validator类 return value
restful-serializer中 字段继承自 ···\rest_framework\fields.py class Field() 没留那么多钩子,可是能够经过覆写to_representation方法实现 须要注意的是,每一个Filed的这个方法作的事情不同
class MyField(serializers.CharField): # 自定义filed def to_representation(self, value): 验证或者其它操做 return str(value)
这个没应用过,但原理是通的。
找钩子函数,定义序列化类时,将钩子函数写进去 这个钩子方法写在序列化类,而非某个字段。
Serializer 为例: Serializer内部能够找到如下3个方法,不用去父类找 is_valid >>> run_validation >>> to_internal_value
for field in fields: validate_method = getattr(self, 'validate_' + field.field_name, None) ... if validate_method is not None: validated_value = validate_method(validated_value) ...
因此钩子方法就是 validate_ + 字段名 + () 传入的是value,return的也是value,中间怎么搞本身发挥
整个钩子方法都在try语句里边,后边有except ValidationError跟着 因此不经过,直接raise ValidationError就好了
【类比form】 也是form级调用is_valid方法时: is_valid()调用self.is_bound 和 se lf.errors self.errors >>> self.full_clean() >>> ① self.clean_fields() ② self.clean_form() >>> clean()#纯钩子 ③ self.post_clean() #纯钩子 2和3是纯钩子,不作讨论 clean_fields() 下 for循环内部 hasattr(self, 'clean%s' % name):#这里的name形参其实就是form的字段名 value = getattr(self, 'clean%s' % name)() self.cleaned_data[name] = value 钩子方法:clean + 字段名 + ()
validate_title() clean_title() 二者都是在验证器/表单级别的类里边,提供了针对具体字段验证的钩子方法
就是这么简单的事情,官方文档的解释也太草率了,还给了个不知所云的even_number
验证器能够是任何可调用对象,在失败时引起 serializers.ValidationError。 def even_number(value): if value % 2 != 0: raise serializers.ValidationError('This field must be an even number.') 你能够经过向 Serializer 子类添加 .validate_<field_name>方法来指定自定义字段级验证。
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination
分页相关的类:···\rest_framework\pagination.py BasePagination 基类 PageNumberPagination 普通分页 LimitOffsetPagination 按照Limit、Offset分页,此时page参数失效 CursorPagination 游标/加密分页
分页里边惟一的亮点:在于游标分页,大幅度提高性能,但必定程度上限制了用户的查询便利性。
class MytestSerialiser(serializers.ModelSerializer): class Meta: model = models.Role fields = "__all__" class MyPageNumberPagination(PageNumberPagination): # 默认每页显示的数据条数 page_size = 4 # 获取URL参数中设置的每页显示数据条数 page_size_query_param = 'page_size' # 获取URL参数中传入的页码key page_query_param = 'page' # 最大支持的每页显示的数据条数 max_page_size = 10 class MyLimitOffsetPagination(LimitOffsetPagination): # 默认每页显示的数据条数 default_limit = 5 # URL中传入的显示数据条数的参数 limit_query_param = 'limit' # URL中传入的数据位置的参数 offset_query_param = 'offset' #第几条 # 最大每页显得条数 max_limit = None class MyCursorPagination(CursorPagination): # URL传入的游标参数 cursor_query_param = 'cursor' # 默认每页显示的数据条数 page_size = 2 # URL传入的每页显示条数的参数 page_size_query_param = 'page_size' # 每页显示数据最大条数 max_page_size = 500 # 根据ID从大到小排列 ordering = "id" class PagerView2(APIView): ''' http://127.0.0.1:8000/api/v1/page/02/ 获取数据 http://127.0.0.1:8000/api/v1/page/02/?page=2 获取具体页码数据 http://127.0.0.1:8000/api/v1/page/02/?page=2&page_size=3 用户还能本身指定每页条数 ''' def get(self, request, *args, **kwargs): roles = models.Role.objects.all() # 实例化分页对象,获取数据库中的分页数据 paginator = MyPageNumberPagination() page_user_list = paginator.paginate_queryset(roles, self.request, view=self) # 分页的方法 # 序列化分页后的对象 serializer = MytestSerialiser(page_user_list, many=True) # 生成分页和数据 response = paginator.get_paginated_response(serializer.data) # 内部执行Response方法 return response
以GenericAPIView为例,继承自APIView。与django的GenericView思路及其类似(鸡肋) 我的认为类通用视图比较鸡肋,并且耦合性太强,能不用就不用 可是在使用视图集时,这东西就必须用了
class View1View(GenericAPIView): # APIView queryset = models.Role.objects.all() serializer_class = PagerSerialiser pagination_class = PageNumberPagination def get(self,request,*args,**kwargs): # 获取数据 roles = self.get_queryset() # models.Role.objects.all() # [1, 1000,] [1,10] pager_roles = self.paginate_queryset(roles) # 序列化 ser = self.get_serializer(instance=pager_roles,many=True) return Response(ser.data)
Django REST framework 容许将一组相关视图的逻辑组合到一个称为 ViewSet 的类中 ViewSet 类只是一种基于类的 View,它不提供任何处理方法,如 .get() 或 .post(),而是提供诸如 .list() 和 .create() 之类的操做。 ViewSet 只在用 .as_view() 方法绑定到最终化视图时作一些相应操做。 一般,不是在 urlconf 中的视图集中明确注册视图,而是使用路由器类注册视图集,这会自动为您肯定 urlconf。
映射关系须要本身写在urlpatterns里:
'get': 'retrieve', 'get': 'list', 'post':'create' 'delete': 'destroy', 'put': 'update', 'patch': 'partial_update',
最全的类继承,就是继承ModelViewSet,包含则增删改查全部的方法
class ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet): """ A viewset that provides default `create()`, `retrieve()`, `update()`, `partial_update()`, `destroy()` and `list()` actions. """ pass
增删改查全继承: class View2View(ModelViewSet): 少来点: class View3View(ListModelMixin,CreateModelMixin,GenericViewSet):
对应路由的写法:
# 若是不使用router ,get的映射要本身写两遍,不带pk参数映射list 带pk映射retrieve # 由于视图集把get方法拆开了,在视图集里对应两个类 url(r'^(?P<version>[v1|v2]+)/viewset/$', views.View2View.as_view({'get': 'list','post':'create'})), url(r'^(?P<version>[v1|v2]+)/viewset/(?P<pk>\d+)/$', views.View2View.as_view({'get': 'retrieve', 'delete': 'destroy', 'put': 'update', 'patch': 'partial_update', })), url(r'^(?P<version>[v1|v2]+)/viewset3/$', views.View3View.as_view({'get': 'list','post':'create'})),
genericviewset的第一个父类 ViewSetMixin 改写了as_view方法 路由规则改变了,因此要本身在url后边加{'get','xxx'}参数
Modelviewset,它继承6个类,功能最全
【小结】 使用的时候 APIView - Genericviewset - modelviewset 功能愈来愈全,代码愈来愈少,耦合愈来愈强
Genericviewset 与增删改查 联合做为父类 也行,灵活应用
补充了一个更细粒度的权限管理,以前是全局权限,引入这个以后能够作对象级别的权限管理
Genericviewset惟一的好处:获取列表/单个对象 作出了区分
这俩东西是掺和在一块的
urlpatterns = [ url(r'^test/$', views.TestView.as_view()), url(r'^test/(?P<pk>[^/.]+)/$', views.TestView.as_view()), url(r'^test\.(?P<format>[a-z0-9]+)$', views.TestView.as_view()), url(r'^test/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)$', views.TestView.as_view()) ]
前边两个是 为列表/对象查询作的区分 后边两个引入的那个format / pk+format 纯粹是为了支持渲染器写的 若是公司项目仅适用json,那渲染器就没什么用了。
urlpatterns = [ url(r'^test/$', views.UserViewSet.as_view({'get': 'list', 'post': 'create'})), url(r'^test/(?P<pk>\d+)/$', views.UserViewSet.as_view( {'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})), ]
from django.conf.urls import url, include from rest_framework import routers from api import views router = routers.DefaultRouter() router.register(r'users', views.UserViewSet) urlpatterns = [ url(r'^', include(router.urls)),
视图函数中:
from rest_framework.viewsets import ModelViewSet from rest_framework import serializers from .. import models class UserSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo fields = "__all__" class UserViewSet(ModelViewSet): queryset = models.UserInfo.objects.all() serializer_class = UserSerializer
include(router.urls) 一句帮咱们生成对应的4种url,省点事
这基本就是restful官网教程的quickstart了,写的是爽了,但在生产环境中彻底没意义。
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
···\site-packages\rest_framework\settings.py 默认的渲染器
'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.TemplateHTMLRenderer', )
自定义: 全局settings里设置RENDERER_CLASSES 值 或者 视图里本身指定 renderer_classes = [JSONRenderer,BrowsableAPIRenderer]
默认就有json啊:这三种方式都能访问
http://127.0.0.1:8000/test/?format=json http://127.0.0.1:8000/test.json http://127.0.0.1:8000/test/
JSONRenderer 对应的 media_type = 'application/json' format = 'json'
BrowsableAPIRenderer 对应的 media_type = 'text/html' format = 'api'
一共有10来种渲染器,每种对应的format字段名称都不同
路由和渲染器从根上说是两码事, 引入渲染器会致使url书写变得很蛋疼,那就再引入个路由器,表面上掩盖一下。
恩,就这样