Tastypie入门小结
Mar 9 2016Tastypie是什么
Tastypie是基于Django的RESTful api开发框架,如果你有一个通过Django实现的网站,那么通过Tastypie写少许的代码就能实现一个全功能的REST api。
我要写些什么
这不是一篇Tastypie的入门教程,如果需要教程,可以从官方文档开始,我写的只在探索Tastypie的过程中我认为值得记录的地方。
认证 authorization
认证是指谁在访问该api,Tastypie的默认认证是Authorization(匿名只读认证),由于我们的业务场景api的使用对象是WEB网站以及APP,而且是以WEB网站优先的,所以计划使用SessionAuthentication与ApiKeyAuthentication。
SessionAuthentication是使用在网站上的,ApiKeyAuthentication可能需要定制,因为我们的OAUTH的token格式需要解密解析,待后续APP开发再进行。
另外,在调试api的时候可能需要使用BasicAuthentication用用户名/密码来认证,requests库可以方便的使用BasicAuthentication,所以写一个Meta
基类来实现。
class BaseMeta:
'''
Resource Meta 基类,把Resource的常用设置写在这里
'''
list_allowed_methods = ['get', 'post']
detail_allowed_methods = ['get', 'post', 'put', 'delete', 'patch']
limit = 20 # 默认列表显示数量
authentication = SessionAuthentication()
always_return_data = True
if settings.DEBUG:
BaseMeta.serializer = PrettyJSONSerializer()
BaseMeta.authentication = MultiAuthentication(SessionAuthentication(), BasicAuthentication())
在定义Resource类时Meta类通过继承BaseMeta来写。
授权 authorization
授权,用户是否有权限访问该api,在已有的权限设计中,我们对每个Django的View都设计了权限,而Tastypie的DjangoAuthorization的是通过在Model中定义的权限来进行授权的。所以定义了一个业务授权的授权类。
class BaseAuthorization(Authorization):
'''
Tastypie 权限验证基类
'''
def __init__(self, *args, **kwargs):
'''
read_list_perm 读列表权限
read_detail_perm 读详情页权限
create_detail_perm 创建对象权限
update_list_perm 更新列表权限
update_detail_perm 更新详情权限
delete_detail_perm 删除对象权限
'''
for perm_name in ('read_list_perm', 'read_detail_perm', 'create_detail_perm',
'update_list_perm', 'update_detail_perm', 'delete_detail_perm'):
setattr(self, perm_name, kwargs.pop(perm_name, None))
super(BaseAuthorization, self).__init__(*args, **kwargs)
def read_list(self, object_list, bundle):
if self.read_list_perm and bundle.request.user.has_perm(self.read_list_perm):
return object_list
raise Unauthorized("You are not allowed to access that resource.")
def read_detail(self, object_list, bundle):
if self.read_detail_perm and bundle.request.user.has_perm(self.read_detail_perm):
return True
raise Unauthorized("You are not allowed to access that resource.")
def create_detail(self, object_list, bundle):
if self.create_detail_perm and bundle.request.user.has_perm(self.create_detail_perm):
return True
raise Unauthorized("You are not allowed to access that resource.")
def update_list(self, object_list, bundle):
if self.update_list_perm and bundle.request.user.has_perm(self.update_list_perm):
return object_list
raise Unauthorized("You are not allowed to access that resource.")
def update_detail(self, object_list, bundle):
if self.update_detail_perm and bundle.request.user.has_perm(self.update_detail_perm):
return True
raise Unauthorized("You are not allowed to access that resource.")
def delete_list(self, object_list, bundle):
raise Unauthorized("Sorry, no deletes.")
def delete_detail(self, object_list, bundle):
if self.delete_detail_perm and bundle.request.user.has_perm(self.delete_detail_perm):
return True
raise Unauthorized("You are not allowed to access that resource.")
# 这样使用
class TestResource(ModelResouce):
class Meta(BaseMeta):
authorization = BaseAuthorization(
read_list_perm='auth.employee_view',
read_detail_perm='auth.employee_view',
create_detail_perm='auth.employee_create',
update_detail_perm='auth.employee_update',
delete_detail_perm='auth.employee_delete'
)
这样在读取或者写入数据的权限就跟业务在View中使用的权限一致了。
捆对象 Bundles
在定制Resource的过程中,不可避免的会使用捆对象,这是一个抽象的概念,代表了获取资源,或者写入资源过程中对单个资源的封装,它有这些属性。
- obj: Model对象
- 在
GET
方法中obj是根据id
来得到的Model对象 - 在
POST
方法中obj是生成的id
为空等待保存的对象 - 在
PUT/PATCH
中obj是某些字段已改变但是没有保存的对象
- 在
- data: 数据字典
- 在
GET
方法中data是等待序列化的字典数据 - 在
POST/PUT/PATCH
法中data是从客户端发送过来的数据
- 在
- request: Django request对象
其它还有related_obj
, related_name
暂时未用到
脱水 dehydrate
类似于Django Form类中clean以及clean_FOO方法的使用,用于处理即将被序列化的数据,即在GET
方法中会调用。如果需要对发送给客户端的数据进行加工,则需要定义相关的脱水方法。
class TestResource(ModelResouce):
def dehydrate(self, bundle):
# 修改数据
bundle.data['title'] = buuidle.data['title'].upper()
# 增加数据
bundle.data['version'] = 'v1'
return bundle
hydrate 注水
与脱水相反,注水是指在POST/PUT/PATCH
方法中对客户端传过来的数据进行加工。
class TestResource(ModelResouce):
def hydrate(self, bundle):
# 修改数据
city_id = bundle.data.pop('insrance_city')
city = City.object.get(pk=city_id)
bundle.obj.city = city
return bundle
过滤查询 filtering
类似于我们业务中的搜索,使用Django Model自带的filter功能来过滤列表。使用方法可以参考文档:
http://django-tastypie.readthedocs.org/en/latest/resources.html#basic-filtering
对于在我们业务中类似于关键字搜索,需要同时查询多个字段的需求,可以考虑重写Resouce中的方法,或者增加方法,增加Meta属性,期望能达到我们正在使用的SearchMixin
的效果。一个重写方法的例子:
class EmployeeInfoResource(ModelResource):
def apply_filters(self, request, applicable_filters):
qs = super(EmployeeInfoResource, self).apply_filters(request, applicable_filters)
keyword = request.GET.get('keyword')
if keyword:
keyword = keyword.strip()
qs = qs.filter(Q(credentials_no__icontains=keyword) | Q(emp_name__icontains=keyword))
return qs
以上就是我在探索Tastypie的过程中一点心得,在实际的开发中,相信除了以资源为核心的api外,还有一些操作类的业务需求api,如果这些api基于Tastypie来写可能会得不偿失,所以还需要对操作类api进行规范。
操作类api
可以考虑返回的Response直接输出JsonResponse,或者直接使用Tastypie定义的各种HTTP状态码Response对象,可以在这里看到。
from tastypie.http import *