需求背景:
使用tastypie原则开发api接口,同时对前面设计的api做适当优化。
tastypie风格接口设计:
-
1.资源类定义:
from tastypie.authorization import Authorization
from tastypie.resources import ModelResource
from controller.natgw import models
class NatgwResource(ModelResource):
class Meta:
authorization = Authorization()
resource_name = 'natgw'
list_allowed_methods = ['get']
queryset = models.NatGw.objects.all()
fields = ['ip', 'status', 'vip', 'ospf_status', 'version', 'time', 'gw_version']
-
2.查询数据列表接口:
uri: /natgw
method: get
上述代码中,fields表示哪些字段需要接口返回;resource_name表示这个资源在url中的名称,跟上述uri的值对应;queryset表示查询的数据库对象;查询接口需要支持按字段过滤,所以需要添加如下代码:
filtering = {
'ip': ('exact', 'contains', 'startswith'),
'status': ('exact', 'in'),
'vip': ('exact', 'contains', 'startswith'),
'ospf_status': ('exact', 'in'),
}
此时如果按照ip模糊匹配196网段的,则url为:/natgw?ip_startswith=196
;
-
3.添加数据接口:
uri: /natgw
method: post
data: {}
上述代码已经支持添加数据的接口调用了,此时data中的字段跟数据库中字段有对应的则可保存到数据库中。现在要求添加数据接口只支持数据库中某些字段设置,其他字段不支持在添加时赋值,则需要做一些处理,代码如下:
def obj_create(self, bundle, **kwargs):
data = copy.deepcopy(bundle.data)
bundle.data.clear() #清空data,只设置需要的字段的值
bundle.data['ip'] = data.get('ip', None)
bundle.data['agent_port'] = data.get('agent_port', 11111)
# remove redundant parameters from bundle.data
logger.info("Creating object: data2 is: %s" % bundle.data)
super(NatgwResource, self).obj_create(bundle, **kwargs)
return
此处应该可以用hydrate处理,但是本人还不是很理解,需要再理解理解。
-
4.查询数据详情接口:
查询数据详情时,只要在查询数据列表接口的uri后追加key值即可,如:
uri: /natgw/{keyvalue}
method: get
此处多主键时该当如何处理?大概uuid做主键可以解决。
-
5.删除数据记录接口:
uri: /natgw/{ip}
method: delete
默认情况下,ip字段不携带时,django框架会清空资源对应的表。此操作极度危险。所以需要对删除操作做一定的处理:
def obj_delete_list(self, bundle, **kwargs):
raise NotFound("The URL provided '%s:%s' was not a link to a valid resource." % (bundle.request.path, bundle.request.method))
-
6.修改数据接口:
uri: /natgw/{ip}
method: put
用put方法修改数据时,如果主键被修改,则被认为是新增记录,所以这里需要处理一下:查看基类ModelResource可知,put_detail方法实现了这一功能,而obj_update方法实现了修改数据的功能,如果要修改的对象不存在,则抛出异常给put_detail方法,put_detail对NotFound类型的Exception做了新增该数据的处理,可以尝试重载put_detail方法,但是实现上感觉又跟基类方法差别太多。对比put方法和patch方法差异知:
def patch_detail(self, request, **kwargs):
"""
Updates a resource in-place.
Calls ``obj_update``.
If the resource is updated, return ``HttpAccepted`` (202 Accepted).
If the resource did not exist, return ``HttpNotFound`` (404 Not Found).
"""
可以尝试使用patch方法看看是否返回对象不存在的错误。验证确实返回404错误。所以修改上述接口定义如下:
uri: /natgw/{ip}
method: patch
-
7.其他类型接口:
在设计开发中往往有一些接口不是如上述那样简单的定义就可以完成功能,需要在uri中携带相关参数表明需要完成什么任务,此时该如何处理呢?
举一个栗子:资源natgw需要单独接口支持enable,那这个接口该如何设计呢?
仔细阅读参考资料中文档,可以找到蛛丝马迹:prepend_urls方法就是用于实现该功能的^^
def prepend_urls(self):
return [
url(r"^(?P<resource_name>%s)/(?P<ip>.*?)/enable%s" % \
(self.Meta.resource_name, trailing_slash()),
self.wrap_view('enable_natgw'),
name="api_enable_natgw")
]
上述代码就可以给natgw增加一个接口,实现方法为enable_natgw。
def enable_natgw(self, request, **kwargs):
logger.info("Starting ebable natgw, request = %s, kwargs = %s." % (request, kwargs))
self.method_check(request, allowed=['put'])
return HttpResponse("OK")
下面使用postman验证一下:
- step1:
uri: 127.0.0.1:8000/api/v1/natgw/10.1.1/enable
method: patch
- step2:
uri: 127.0.0.1:8000/api/v1/natgw/10.1.1/enable
method: patch
- 返回值:1.PUT 2.OK
- 日志打印内容:
INFO 2017-03-03 14:21:28,046 resources 12664 12048 Starting ebable natgw, request = <WSGIRequest: PATCH '/api/v1/natgw/10.1.1/enable'>, kwargs = {u'ip': u'10.1.1', u'api_name': u'v1', u'resource_name': u'natgw'}.
WARNING 2017-03-03 14:21:28,046 basehttp 12664 12048 "PATCH /api/v1/natgw/10.1.1/enable HTTP/1.1" 405 3
INFO 2017-03-03 14:21:32,947 resources 12664 13052 Starting ebable natgw, request = <WSGIRequest: PUT '/api/v1/natgw/10.1.1/enable'>, kwargs = {u'ip': u'10.1.1', u'api_name': u'v1', u'resource_name': u'natgw'}.
INFO 2017-03-03 14:21:32,950 basehttp 12664 13052 "PUT /api/v1/natgw/10.1.1/enable HTTP/1.1" 200 2
综上可知自定义的接口以及实现,想要实现相关功能就可以在enable_natgw方法中实现啦^^