[TOC]
学习路径
1. 序列化
- 对象处理
snippet = Snippet(code='foo = "bar"\n')
snippet.save()
snippet = Snippet(code='print "hello, world"\n')
snippet.save()
- 实例序列化
serializer = SnippetSerializer(snippet)
serializer.data
# {'id': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}
- 转json
content = JSONRenderer().render(serializer.data)
content
# '{"id": 2, "title": "", "code": "print \\"hello, world\\"\\n", "linenos": false, "language": "python", "style": "friendly"}'
2. 反序列化
stream = BytesIO(content)
data = JSONParser().parse(stream)
- 恢复对象实例
serializer = SnippetSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# OrderedDict([('title', ''), ('code', 'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
serializer.save()
#
- (可选)序列化查询结果集(querysets)而不是模型实例
serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data
# [OrderedDict([('id', 1), ('title', u''), ('code', u'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]),
OrderedDict([('id', 2), ('title', u''), ('code', u'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]),
OrderedDict([('id', 3), ('title', u''), ('code', u'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]
3. 基于类视图CBV
- 继承APIView
简单的继承APIView
class SnippetList(APIView):
"""
列出所有的snippets或者创建一个新的snippet。
"""
def get(self, request, format=None):
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
-
使用混合
继承
mixins.UpdateModelMixin|DestroyModelMixin
、generics.GenericaAPIView
class SnippetDetail(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView): queryset = Snippet.objects.all() serializer_class = SnippetSerializer def get(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs)
-
通用的基于类的视图
更换 genericView → generics.RetrieveUpdateDestroyAPIView
from snippets.models import Snippet from snippets.serializers import SnippetSerializer from rest_framework import generics class SnippetList(generics.ListCreateAPIView): queryset = Snippet.objects.all() serializer_class = SnippetSerializer class SnippetDetail(generics.RetrieveUpdateDestroyAPIView): queryset = Snippet.objects.all() serializer_class = SnippetSerializer
4. 认证和权限
前提条件:
- 代码片段始终与创建者相关联。
- 只有通过身份验证的用户可以创建片段。
- 只有代码片段的创建者可以更新或删除它。
- 未经身份验证的请求应具有完全只读访问权限。
操作步骤:
model添加
owner
、highlighted
字段model中增加
.save()
方法-
为用户模型增加路径
serializers.py 中追加
添加基于类的通用视图
-
添加urls.py
url(r'^users/$', views.UserList.as_view()), url(r'^users/(?P
[0-9]+)/$', views.UserDetail.as_view()),
-
将snippet和用户关联
- snippeList 追加
perform_create
方法 - 更新序列化器
- snippeList 追加
添加视图所需权限
给Browsable API添加登录
-
对象级别权限控制
- 新增
permissions.py
- 新增
使用API 进行身份验证
5. 视图集ViewSets和路由器
前言:
ViewSet
类与View
类几乎相同,不同之处在于它们提供诸如read
或update
之类的操作,而不是get
或put
等方法处理程序。最后一个
ViewSet
类只绑定到一组方法处理程序,当它被实例化成一组视图的时候,通常通过使用一个Router
类来处理自己定义URL conf的复杂性。
使用ViewSets重构
-
将viewSets 绑定到url
-
显示
from snippets.views import SnippetViewSet, UserViewSet, api_root from rest_framework import renderers snippet_list = SnippetViewSet.as_view({ 'get': 'list', 'post': 'create' }) snippet_detail = SnippetViewSet.as_view({ 'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy' }) snippet_highlight = SnippetViewSet.as_view({ 'get': 'highlight' }, renderer_classes=[renderers.StaticHTMLRenderer]) user_list = UserViewSet.as_view({ 'get': 'list' }) user_detail = UserViewSet.as_view({ 'get': 'retrieve' })
-
具体视图
urlpatterns = format_suffix_patterns([ url(r'^$', api_root), url(r'^snippets/$', snippet_list, name='snippet-list'), url(r'^snippets/(?P
[0-9]+)/$', snippet_detail, name='snippet-detail'), url(r'^snippets/(?P [0-9]+)/highlight/$', snippet_highlight, name='snippet-highlight'), url(r'^users/$', user_list, name='user-list'), url(r'^users/(?P [0-9]+)/$', user_detail, name='user-detail') ]) -
使用路由器
# 创建路由器并注册我们的视图。 router = DefaultRouter() router.register(r'snippets', views.SnippetViewSet) router.register(r'users', views.UserViewSet) # API URL现在由路由器自动确定。 # 另外,我们还要包含可浏览的API的登录URL。 urlpatterns = [ url(r'^', include(router.urls)), url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) ]
-
5.1 视图(views)vs视图集(viewsets)之间的权衡
使用视图集可以是一个非常有用的抽象。它有助于确保URL约定在你的API中保持一致,最大限度地减少编写所需的代码量,让你能够专注于API提供的交互和表示,而不是URLconf的细节。
这并不意味着采用视图集总是正确的方法。在使用基于类的视图而不是基于函数的视图时,有一个类似的权衡要考虑。使用视图集不像单独构建视图那样明确。
6. 概要和客户端库
- 概要: 机器可读文档、描述可用api路径 ,其urls以及 支持的操作
- 概要: 自动生成文档的有用工具,也可以用于驱动可以与api进行交互的动态客户端库
6.1 Core API
为了提供概要支持REST框架使用Core API。
Core API是用于描述API的文档规范。它用于提供可用路径的内部表示形式和API公开的可能的交互。它可以用于服务器端或客户端。
6.2 添加概要
REST框架支持明确定义的概要视图或自动生成的概要。由于我们使用的是视图集和路由器,我们可以简单地使用自动概要生成。
你需要安装coreapi
python包才能包含API概要。
$ pip install coreapi
现在我们可以通过在URL配置中包含一个自动生成的概要视图来为API添加概要。
from rest_framework.schemas import get_schema_view
schema_view = get_schema_view(title='Pastebin API')
urlpatterns = [
url('^schema/$', schema_view),
...
]
如果你在浏览器中访问API根路径,那么你现在应该可以看到corejson
表示形式是一个可用选项。
6.3 使用命令行客户端
现在我们的API暴露了一个概要路径,我们可以使用一个动态的客户端库与API进行交互。为了演示这个,我们来使用Core API命令行客户端。
命令行客户端作为一个coreapi-cli
包提供:
$ pip install coreapi-cli
现在检查它在命令行上是否可用...
$ coreapi
Usage: coreapi [OPTIONS] COMMAND [ARGS]...
Command line client for interacting with CoreAPI services.
Visit http://www.coreapi.org for more information.
Options:
--version Display the package version number.
--help Show this message and exit.
Commands:
...
首先,我们将使用命令行客户端加载API概要。
$ coreapi get http://127.0.0.1:8000/schema/
snippets: {
highlight(id)
list()
read(id)
}
users: {
list()
read(id)
}
我们还没有认证,所以现在我们只能看到只读路径,这与我们设置的API权限是一致的。
6.4 验证我们的客户端
如果我们想要创建,编辑和删除代码片段,我们需要进行有效性用户身份验证。在这种情况下,我们只需使用基本的auth。
请确保使用实际的用户名和密码替换下面的
和
。
$ coreapi credentials add 127.0.0.1 : --auth basic
Added credentials
127.0.0.1 "Basic <...>"
现在,如果我们再次提取概要,我们应该能够看到一组可用的交互。
$ coreapi reload