# Flask-RESTful
**Flask-RESTful** 是一個 Flask 擴展,它添加了快速構建 REST APIs 的支持。它當然也是一個能夠跟你現有的ORM/庫協同工作的輕量級的擴展。Flask-RESTful 鼓勵以最小設置的最佳實踐。如果你熟悉 Flask 的話,Flask-RESTful 應該很容易上手。
# 安裝
使用 `pip` 安裝 Flask-RESTful:
```
pip install flask-restful
```
開發的版本可以從 [GitHub 上的頁面](http://github.com/twilio/flask-restful) 下載
```
git clone https://github.com/twilio/flask-restful.git
cd flask-restful
python setup.py develop
```
Flask-RESTful 有如下的依賴包(如果你使用 `pip`,依賴包會自動地安裝):
* [Flask](http://flask.pocoo.org) 版本 0.8 或者更高
Flask-RESTful 要求 Python 版本為 2.6, 2.7, 或者 3.3。
# 快速入門
是時候編寫你第一個 REST API。本指南假設你對 [Flask](http://flask.pocoo.org) 有一定的認識,并且已經安裝了 Flask 和 Flask-RESTful。如果還沒有安裝的話,可以依照 [_安裝_](installation.html#installation) 章節的步驟安裝。
## 一個最小的 API
一個最小的 Flask-RESTful API 像這樣:
```
from flask import Flask
from flask.ext import restful
app = Flask(__name__)
api = restful.Api(app)
class HelloWorld(restful.Resource):
def get(self):
return {'hello': 'world'}
api.add_resource(HelloWorld, '/')
if __name__ == '__main__':
app.run(debug=True)
```
把上述代碼保存為 api.py 并且在你的 Python 解釋器中運行它。需要注意地是我們已經啟用了 [Flask 調試](http://flask.pocoo.org/docs/quickstart/#debug-mode) 模式,這種模式提供了代碼的重載以及更好的錯誤信息。調試模式絕不能在生產環境下使用。
```
$ python api.py
* Running on http://127.0.0.1:5000/
```
現在打開一個新的命令行窗口使用 curl 測試你的 API:
```
$ curl http://127.0.0.1:5000/
{"hello": "world"}
```
## 資源豐富的路由(Resourceful Routing)
Flask-RESTful 提供的最主要的基礎就是資源(resources)。資源(Resources)是構建在 [Flask 可拔插視圖](http://flask.pocoo.org/docs/views/) 之上,只要在你的資源(resource)上定義方法就能夠容易地訪問多個 HTTP 方法。一個待辦事項應用程序的基本的 CRUD 資源看起來像這樣:
```
from flask import Flask, request
from flask.ext.restful import Resource, Api
app = Flask(__name__)
api = Api(app)
todos = {}
class TodoSimple(Resource):
def get(self, todo_id):
return {todo_id: todos[todo_id]}
def put(self, todo_id):
todos[todo_id] = request.form['data']
return {todo_id: todos[todo_id]}
api.add_resource(TodoSimple, '/<string:todo_id>')
if __name__ == '__main__':
app.run(debug=True)
```
你可以嘗試這樣:
```
$ curl http://localhost:5000/todo1 -d "data=Remember the milk" -X PUT
{"todo1": "Remember the milk"}
$ curl http://localhost:5000/todo1
{"todo1": "Remember the milk"}
$ curl http://localhost:5000/todo2 -d "data=Change my brakepads" -X PUT
{"todo2": "Change my brakepads"}
$ curl http://localhost:5000/todo2
{"todo2": "Change my brakepads"}
```
或者如果你安裝了 requests 庫的話,可以從 python shell 中運行:
```
>>> from requests import put, get
>>> put('http://localhost:5000/todo1', data={'data': 'Remember the milk'}).json()
{u'todo1': u'Remember the milk'}
>>> get('http://localhost:5000/todo1').json()
{u'todo1': u'Remember the milk'}
>>> put('http://localhost:5000/todo2', data={'data': 'Change my brakepads'}).json()
{u'todo2': u'Change my brakepads'}
>>> get('http://localhost:5000/todo2').json()
{u'todo2': u'Change my brakepads'}
```
Flask-RESTful 支持視圖方法多種類型的返回值。同 Flask 一樣,你可以返回任一迭代器,它將會被轉換成一個包含原始 Flask 響應對象的響應。Flask-RESTful 也支持使用多個返回值來設置響應代碼和響應頭,如下所示:
```
class Todo1(Resource):
def get(self):
# Default to 200 OK
return {'task': 'Hello world'}
class Todo2(Resource):
def get(self):
# Set the response code to 201
return {'task': 'Hello world'}, 201
class Todo3(Resource):
def get(self):
# Set the response code to 201 and return custom headers
return {'task': 'Hello world'}, 201, {'Etag': 'some-opaque-string'}
```
## 端點(Endpoints)
很多時候在一個 API 中,你的資源可以通過多個 URLs 訪問。你可以把多個 URLs 傳給 Api 對象的 [`Api.add_resource()`](api.html#flask.ext.restful.Api.add_resource "flask.ext.restful.Api.add_resource") 方法。每一個 URL 都能訪問到你的 [`Resource`](api.html#flask.ext.restful.Resource "flask.ext.restful.Resource")
```
api.add_resource(HelloWorld,
'/',
'/hello')
```
你也可以為你的資源方法指定 endpoint 參數。
```
api.add_resource(Todo,
'/todo/<int:todo_id>', endpoint='todo_ep')
```
## 參數解析
盡管 Flask 能夠簡單地訪問請求數據(比如查詢字符串或者 POST 表單編碼的數據),驗證表單數據仍然很痛苦。Flask-RESTful 內置了支持驗證請求數據,它使用了一個類似 [argparse](http://docs.python.org/dev/library/argparse.html) 的庫。
```
from flask.ext.restful import reqparse
parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, help='Rate to charge for this resource')
args = parser.parse_args()
```
需要注意地是與 argparse 模塊不同,`reqparse.RequestParser.parse_args()` 返回一個 Python 字典而不是一個自定義的數據結構。
使用 [`reqparse`](api.html#module-reqparse "reqparse") 模塊同樣可以自由地提供聰明的錯誤信息。如果參數沒有通過驗證,Flask-RESTful 將會以一個 400 錯誤請求以及高亮的錯誤信息回應。
```
$ curl -d 'rate=foo' http://127.0.0.1:5000/
{'status': 400, 'message': 'foo cannot be converted to int'}
```
[`inputs`](api.html#module-flask.ext.restful.inputs "flask.ext.restful.inputs") 模塊提供了許多的常見的轉換函數,比如 [`inputs.date()`](api.html#flask.ext.restful.inputs.date "flask.ext.restful.inputs.date") 和 [`inputs.url()`](api.html#flask.ext.restful.inputs.url "flask.ext.restful.inputs.url")。
使用 `strict=True` 調用 `parse_args` 能夠確保當請求包含你的解析器中未定義的參數的時候會拋出一個異常。
> args = parser.parse_args(strict=True)
## 數據格式化
默認情況下,在你的返回迭代中所有字段將會原樣呈現。盡管當你剛剛處理 Python 數據結構的時候,覺得這是一個偉大的工作,但是當實際處理它們的時候,會覺得十分沮喪和枯燥。為了解決這個問題,Flask-RESTful 提供了 `fields` 模塊和 [`marshal_with()`](api.html#flask.ext.restful.marshal_with "flask.ext.restful.marshal_with") 裝飾器。類似 Django ORM 和 WTForm,你可以使用 fields 模塊來在你的響應中格式化結構。
```
from collections import OrderedDict
from flask.ext.restful import fields, marshal_with
resource_fields = {
'task': fields.String,
'uri': fields.Url('todo_ep')
}
class TodoDao(object):
def __init__(self, todo_id, task):
self.todo_id = todo_id
self.task = task
# This field will not be sent in the response
self.status = 'active'
class Todo(Resource):
@marshal_with(resource_fields)
def get(self, **kwargs):
return TodoDao(todo_id='my_todo', task='Remember the milk')
```
上面的例子接受一個 python 對象并準備將其序列化。[`marshal_with()`](api.html#flask.ext.restful.marshal_with "flask.ext.restful.marshal_with") 裝飾器將會應用到由 `resource_fields` 描述的轉換。從對象中提取的唯一字段是 `task`。`fields.Url` 域是一個特殊的域,它接受端點(endpoint)名稱作為參數并且在響應中為該端點生成一個 URL。許多你需要的字段類型都已經包含在內。請參閱 `fields` 指南獲取一個完整的列表。
## 完整的例子
在 api.py 中保存這個例子
```
from flask import Flask
from flask.ext.restful import reqparse, abort, Api, Resource
app = Flask(__name__)
api = Api(app)
TODOS = {
'todo1': {'task': 'build an API'},
'todo2': {'task': '?????'},
'todo3': {'task': 'profit!'},
}
def abort_if_todo_doesnt_exist(todo_id):
if todo_id not in TODOS:
abort(404, message="Todo {} doesn't exist".format(todo_id))
parser = reqparse.RequestParser()
parser.add_argument('task', type=str)
# Todo
# show a single todo item and lets you delete them
class Todo(Resource):
def get(self, todo_id):
abort_if_todo_doesnt_exist(todo_id)
return TODOS[todo_id]
def delete(self, todo_id):
abort_if_todo_doesnt_exist(todo_id)
del TODOS[todo_id]
return '', 204
def put(self, todo_id):
args = parser.parse_args()
task = {'task': args['task']}
TODOS[todo_id] = task
return task, 201
# TodoList
# shows a list of all todos, and lets you POST to add new tasks
class TodoList(Resource):
def get(self):
return TODOS
def post(self):
args = parser.parse_args()
todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1
todo_id = 'todo%i' % todo_id
TODOS[todo_id] = {'task': args['task']}
return TODOS[todo_id], 201
##
## Actually setup the Api resource routing here
##
api.add_resource(TodoList, '/todos')
api.add_resource(Todo, '/todos/<todo_id>')
if __name__ == '__main__':
app.run(debug=True)
```
用法示例
```
$ python api.py
* Running on http://127.0.0.1:5000/
* Restarting with reloader
```
獲取列表
```
$ curl http://localhost:5000/todos
{"todo1": {"task": "build an API"}, "todo3": {"task": "profit!"}, "todo2": {"task": "?????"}}
```
獲取一個單獨的任務
```
$ curl http://localhost:5000/todos/todo3
{"task": "profit!"}
```
刪除一個任務
```
$ curl http://localhost:5000/todos/todo2 -X DELETE -v
> DELETE /todos/todo2 HTTP/1.1
> User-Agent: curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3
> Host: localhost:5000
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 204 NO CONTENT
< Content-Type: application/json
< Content-Length: 0
< Server: Werkzeug/0.8.3 Python/2.7.2
< Date: Mon, 01 Oct 2012 22:10:32 GMT
```
增加一個新的任務
```
$ curl http://localhost:5000/todos -d "task=something new" -X POST -v
> POST /todos HTTP/1.1
> User-Agent: curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3
> Host: localhost:5000
> Accept: */*
> Content-Length: 18
> Content-Type: application/x-www-form-urlencoded
>
* HTTP 1.0, assume close after body
< HTTP/1.0 201 CREATED
< Content-Type: application/json
< Content-Length: 25
< Server: Werkzeug/0.8.3 Python/2.7.2
< Date: Mon, 01 Oct 2012 22:12:58 GMT
<
* Closing connection #0
{"task": "something new"}
```
更新一個任務
```
$ curl http://localhost:5000/todos/todo3 -d "task=something different" -X PUT -v
> PUT /todos/todo3 HTTP/1.1
> Host: localhost:5000
> Accept: */*
> Content-Length: 20
> Content-Type: application/x-www-form-urlencoded
>
* HTTP 1.0, assume close after body
< HTTP/1.0 201 CREATED
< Content-Type: application/json
< Content-Length: 27
< Server: Werkzeug/0.8.3 Python/2.7.3
< Date: Mon, 01 Oct 2012 22:13:00 GMT
<
* Closing connection #0
{"task": "something different"}
```
# 請求解析
Flask-RESTful 的請求解析接口是模仿 `argparse` 接口。它設計成提供簡單并且統一的訪問 Flask 中 `flask.request` 對象里的任何變量的入口。
## 基本參數
這里是請求解析一個簡單的例子。它尋找在 `flask.Request.values` 字典里的兩個參數。一個類型為 `int`,另一個的類型是 `str`
```
from flask.ext.restful import reqparse
parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, help='Rate cannot be converted')
parser.add_argument('name', type=str)
args = parser.parse_args()
```
如果你指定了 help 參數的值,在解析的時候當類型錯誤被觸發的時候,它將會被作為錯誤信息給呈現出來。如果你沒有指定 help 信息的話,默認行為是返回類型錯誤本身的信息。
默認下,arguments **不是** 必須的。另外,在請求中提供的參數不屬于 RequestParser 的一部分的話將會被忽略。
另請注意:在請求解析中聲明的參數如果沒有在請求本身設置的話將默認為 `None`。
## 必需的參數
要求一個值傳遞的參數,只需要添加 `required=True` 來調用 `add_argument()`。
```
parser.add_argument('name', type=str, required=True,
help="Name cannot be blank!")
```
## 多個值&列表
如果你要接受一個鍵有多個值的話,你可以傳入 `action='append'`
```
parser.add_argument('name', type=str, action='append')
```
這將讓你做出這樣的查詢
```
curl http://api.example.com -d "Name=bob" -d "Name=sue" -d "Name=joe"
```
你的參數將會像這樣
```
args = parser.parse_args()
args['name'] # ['bob', 'sue', 'joe']
```
## 其它目標(Destinations)
如果由于某種原因,你想要以不同的名稱存儲你的參數一旦它被解析的時候,你可以使用 `dest` kwarg。
```
parser.add_argument('name', type=str, dest='public_name')
args = parser.parse_args()
args['public_name']
```
## 參數位置
默認下,`RequestParser` 試著從 `flask.Request.values`,以及 `flask.Request.json` 解析值。
在 `add_argument()` 中使用 `location` 參數可以指定解析參數的位置。`flask.Request` 中任何變量都能被使用。例如:
```
# Look only in the POST body
parser.add_argument('name', type=int, location='form')
# Look only in the querystring
parser.add_argument('PageSize', type=int, location='args')
# From the request headers
parser.add_argument('User-Agent', type=str, location='headers')
# From http cookies
parser.add_argument('session_id', type=str, location='cookies')
# From file uploads
parser.add_argument('picture', type=werkzeug.datastructures.FileStorage, location='files')
```
## 多個位置
通過傳入一個列表到 `location` 中可以指定 **多個** 參數位置:
```
parser.add_argument('text', location=['headers', 'values'])
```
列表中最后一個優先出現在結果集中。(例如:location=[‘headers’, ‘values’],解析后 ‘values’ 的結果會在 ‘headers’ 前面)
## 繼承解析
往往你會為你編寫的每個資源編寫不同的解析器。這樣做的問題就是如果解析器具有共同的參數。不是重寫,你可以編寫一個包含所有共享參數的父解析器接著使用 `copy()` 擴充它。你也可以使用 `replace_argument()` 覆蓋父級的任何參數,或者使用 `remove_argument()` 完全刪除參數。 例如:
```
from flask.ext.restful import RequestParser
parser = RequestParser()
parser.add_argument('foo', type=int)
parser_copy = parser.copy()
parser_copy.add_argument('bar', type=int)
# parser_copy has both 'foo' and 'bar'
parser_copy.replace_argument('foo', type=str, required=True, location='json')
# 'foo' is now a required str located in json, not an int as defined
# by original parser
parser_copy.remove_argument('foo')
# parser_copy no longer has 'foo' argument
```
# 輸出字段
Flask-RESTful 提供了一個簡單的方式來控制在你的響應中實際呈現什么數據。使用 `fields` 模塊,你可以使用在你的資源里的任意對象(ORM 模型、定制的類等等)并且 `fields` 讓你格式化和過濾響應,因此您不必擔心暴露內部數據結構。
當查詢你的代碼的時候,哪些數據會被呈現以及它們如何被格式化是很清楚的。
## 基本用法
你可以定義一個字典或者 `fields` 的 OrderedDict 類型,OrderedDict 類型是指鍵名是要呈現的對象的屬性或鍵的名稱,鍵值是一個類,該類格式化和返回的該字段的值。這個例子有三個字段,兩個是字符串(Strings)以及一個是日期時間(DateTime),格式為 RFC 822 日期字符串(同樣也支持 ISO 8601)
```
from flask.ext.restful import Resource, fields, marshal_with
resource_fields = {
'name': fields.String,
'address': fields.String,
'date_updated': fields.DateTime(dt_format='rfc822'),
}
class Todo(Resource):
@marshal_with(resource_fields, envelope='resource')
def get(self, **kwargs):
return db_get_todo() # Some function that queries the db
```
這個例子假設你有一個自定義的數據庫對象(`todo`),它具有屬性:`name`, `address`, 以及 `date_updated`。該對象上任何其它的屬性可以被認為是私有的不會在輸出中呈現出來。一個可選的 `envelope` 關鍵字參數被指定為封裝結果輸出。
裝飾器 `marshal_with` 是真正接受你的數據對象并且過濾字段。`marshal_with` 能夠在單個對象,字典,或者列表對象上工作。
注意:marshal_with 是一個很便捷的裝飾器,在功能上等效于如下的 `return marshal(db_get_todo(), resource_fields), 200`。這個明確的表達式能用于返回 200 以及其它的 HTTP 狀態碼作為成功響應(錯誤響應見 `abort`)。
## 重命名屬性
很多時候你面向公眾的字段名稱是不同于內部的屬性名。使用 `attribute` 可以配置這種映射。
```
fields = {
'name': fields.String(attribute='private_name'),
'address': fields.String,
}
```
lambda 也能在 `attribute` 中使用
```
fields = {
'name': fields.String(attribute=lambda x: x._private_name),
'address': fields.String,
}
```
## 默認值
如果由于某種原因你的數據對象中并沒有你定義的字段列表中的屬性,你可以指定一個默認值而不是返回 `None`。
```
fields = {
'name': fields.String(default='Anonymous User'),
'address': fields.String,
}
```
## 自定義字段&多個值
有時候你有你自己定義格式的需求。你可以繼承 `fields.Raw` 類并且實現格式化函數。當一個屬性存儲多條信息的時候是特別有用的。例如,一個位域(bit-field)各位代表不同的值。你可以使用 `fields` 復用一個單一的屬性到多個輸出值(一個屬性在不同情況下輸出不同的結果)。
這個例子假設在 `flags` 屬性的第一位標志著一個“正常”或者“迫切”項,第二位標志著“讀”與“未讀”。這些項可能很容易存儲在一個位字段,但是可讀性不高。轉換它們使得具有良好的可讀性是很容易的。
```
class UrgentItem(fields.Raw):
def format(self, value):
return "Urgent" if value & 0x01 else "Normal"
class UnreadItem(fields.Raw):
def format(self, value):
return "Unread" if value & 0x02 else "Read"
fields = {
'name': fields.String,
'priority': UrgentItem(attribute='flags'),
'status': UnreadItem(attribute='flags'),
}
```
## Url & 其它具體字段
Flask-RESTful 包含一個特別的字段,`fields.Url`,即為所請求的資源合成一個 uri。這也是一個好示例,它展示了如何添加并不真正在你的數據對象中存在的數據到你的響應中。
```
class RandomNumber(fields.Raw):
def output(self, key, obj):
return random.random()
fields = {
'name': fields.String,
# todo_resource is the endpoint name when you called api.add_resource()
'uri': fields.Url('todo_resource'),
'random': RandomNumber,
}
```
默認情況下,`fields.Url` 返回一個相對的 uri。為了生成包含協議(scheme),主機名以及端口的絕對 uri,需要在字段聲明的時候傳入 `absolute=True`。傳入 `scheme` 關鍵字參數可以覆蓋默認的協議(scheme):
```
fields = {
'uri': fields.Url('todo_resource', absolute=True)
'https_uri': fields.Url('todo_resource', absolute=True, scheme='https')
}
```
## 復雜結構
你可以有一個扁平的結構,marshal_with 將會把它轉變為一個嵌套結構
```
>>> from flask.ext.restful import fields, marshal
>>> import json
>>>
>>> resource_fields = {'name': fields.String}
>>> resource_fields['address'] = {}
>>> resource_fields['address']['line 1'] = fields.String(attribute='addr1')
>>> resource_fields['address']['line 2'] = fields.String(attribute='addr2')
>>> resource_fields['address']['city'] = fields.String
>>> resource_fields['address']['state'] = fields.String
>>> resource_fields['address']['zip'] = fields.String
>>> data = {'name': 'bob', 'addr1': '123 fake street', 'addr2': '', 'city': 'New York', 'state': 'NY', 'zip': '10468'}
>>> json.dumps(marshal(data, resource_fields))
'{"name": "bob", "address": {"line 1": "123 fake street", "line 2": "", "state": "NY", "zip": "10468", "city": "New York"}}'
```
注意:address 字段并不真正地存在于數據對象中,但是任何一個子字段(sub-fields)可以直接地訪問對象的屬性,就像沒有嵌套一樣。
## 列表字段
你也可以把字段解組(unmarshal)成列表
```
>>> from flask.ext.restful import fields, marshal
>>> import json
>>>
>>> resource_fields = {'name': fields.String, 'first_names': fields.List(fields.String)}
>>> data = {'name': 'Bougnazal', 'first_names' : ['Emile', 'Raoul']}
>>> json.dumps(marshal(data, resource_fields))
>>> '{"first_names": ["Emile", "Raoul"], "name": "Bougnazal"}'
```
## 高級:嵌套字段
盡管使用字典套入字段能夠使得一個扁平的數據對象變成一個嵌套的響應,你可以使用 `Nested` 解組(unmarshal)嵌套數據結構并且合適地呈現它們。
```
>>> from flask.ext.restful import fields, marshal
>>> import json
>>>
>>> address_fields = {}
>>> address_fields['line 1'] = fields.String(attribute='addr1')
>>> address_fields['line 2'] = fields.String(attribute='addr2')
>>> address_fields['city'] = fields.String(attribute='city')
>>> address_fields['state'] = fields.String(attribute='state')
>>> address_fields['zip'] = fields.String(attribute='zip')
>>>
>>> resource_fields = {}
>>> resource_fields['name'] = fields.String
>>> resource_fields['billing_address'] = fields.Nested(address_fields)
>>> resource_fields['shipping_address'] = fields.Nested(address_fields)
>>> address1 = {'addr1': '123 fake street', 'city': 'New York', 'state': 'NY', 'zip': '10468'}
>>> address2 = {'addr1': '555 nowhere', 'city': 'New York', 'state': 'NY', 'zip': '10468'}
>>> data = { 'name': 'bob', 'billing_address': address1, 'shipping_address': address2}
>>>
>>> json.dumps(marshal_with(data, resource_fields))
'{"billing_address": {"line 1": "123 fake street", "line 2": null, "state": "NY", "zip": "10468", "city": "New York"}, "name": "bob", "shipping_address": {"line 1": "555 nowhere", "line 2": null, "state": "NY", "zip": "10468", "city": "New York"}}'
```
此示例使用兩個嵌套字段。`Nested` 構造函數把字段的字典作為子字段(sub-fields)來呈現。使用 `Nested` 和之前例子中的嵌套字典之間的重要區別就是屬性的上下文。在本例中 “billing_address” 是一個具有自己字段的復雜的對象,傳遞給嵌套字段的上下文是子對象(sub-object),而不是原來的“數據”對象。換句話說,`data.billing_address.addr1` 是在這里的范圍(譯者:這里是直譯),然而在之前例子中的 `data.addr1` 是位置屬性。記住:嵌套和列表對象創建一個新屬性的范圍。
# 擴展 Flask-RESTful
我們認識到每一個人在 REST 框架上有著不同的需求。Flask-RESTful 試圖盡可能的靈活,但是有時候你可能會發現內置的功能不足夠滿足你的需求。Flask-RESTful 有幾個不同的擴展點,這些擴展在這種情況下會有幫助。
## 內容協商
開箱即用,Flask-RESTful 僅配置為支持 JSON。我們做出這個決定是為了給 API 維護者完全控制 API 格式支持,因此一年來的路上,你不必支持那些使用 API 且用 CSV 表示的人們,甚至你都不知道他們的存在。要添加其它的 mediatypes 到你的 API 中,你需要在 [`Api`](api.html#flask.ext.restful.Api "flask.ext.restful.Api") 對象中聲明你支持的表示。
```
app = Flask(__name__)
api = restful.Api(app)
@api.representation('application/json')
def output_json(data, code, headers=None):
resp = make_response(json.dumps(data), code)
resp.headers.extend(headers or {})
return resp
```
這些表示函數必須返回一個 Flask `Response` 對象。
## 自定義字段 & 輸入
一種最常見的 Flask-RESTful 附件功能就是基于你自己數據類型的數據來定義自定義的類型或者字段。
### 字段
自定義輸出字段讓你無需直接修改內部對象執行自己的輸出格式。所有你必須做的就是繼承 `Raw` 并且實現 `format()` 方法:
```
class AllCapsString(fields.Raw):
def format(self, value):
return value.upper()
# example usage
fields = {
'name': fields.String,
'all_caps_name': AllCapsString(attribute=name),
}
```
### 輸入
對于解析參數,你可能要執行自定義驗證。創建你自己的輸入類型讓你輕松地擴展請求解析。
```
def odd_number(value):
if value % 2 == 0:
raise ValueError("Value is not odd")
return value
```
請求解析器在你想要在錯誤消息中引用名稱的情況下將也會允許你訪問參數的名稱。
```
def odd_number(value, name):
if value % 2 == 0:
raise ValueError("The parameter '{}' is not odd. You gave us the value: {}".format(name, value))
return value
```
你還可以將公開的參數轉換為內部表示:
```
# maps the strings to their internal integer representation
# 'init' => 0
# 'in-progress' => 1
# 'completed' => 2
def task_status(value):
statuses = [u"init", u"in-progress", u"completed"]
return statuses.index(value)
```
然后你可以在你的 RequestParser 中使用這些自定義輸入類型:
```
parser = reqparse.RequestParser()
parser.add_argument('OddNumber', type=odd_number)
parser.add_argument('Status', type=task_status)
args = parser.parse_args()
```
## 響應格式
為了支持其它的表示(像 XML,CSV,HTML),你可以使用 [`representation()`](api.html#flask.ext.restful.Api.representation "flask.ext.restful.Api.representation") 裝飾器。你需要在你的 API 中引用它。
```
api = restful.Api(app)
@api.representation('text/csv')
def output_csv(data, code, headers=None):
pass
# implement csv output!
```
這些輸出函數有三個參數,`data`,`code`,以及 `headers`。
`data` 是你從你的資源方法返回的對象,`code` 是預計的 HTTP 狀態碼,`headers` 是設置在響應中任意的 HTTP 頭。你的輸出函數應該返回一個 Flask 響應對象。
```
def output_json(data, code, headers=None):
"""Makes a Flask response with a JSON encoded body"""
resp = make_response(json.dumps(data), code)
resp.headers.extend(headers or {})
return resp
```
另外一種實現這一點的就是繼承 [`Api`](api.html#flask.ext.restful.Api "flask.ext.restful.Api") 類并且提供你自己輸出函數。
```
class Api(restful.Api):
def __init__(self, *args, **kwargs):
super(Api, self).__init__(*args, **kwargs)
self.representations = {
'application/xml': output_xml,
'text/html': output_html,
'text/csv': output_csv,
'application/json': output_json,
}
```
## 資源方法裝飾器
[`Resource()`](api.html#flask.ext.restful.Resource "flask.ext.restful.Resource") 有一個叫做 method_decorators 的屬性。你可以繼承 Resource 并且添加你自己的裝飾器,該裝飾器將會被添加到資源里面所有 `method` 函數。舉例來說,如果你想要為每一個請求建立自定義認證。
```
def authenticate(func):
@wraps(func)
def wrapper(*args, **kwargs):
if not getattr(func, 'authenticated', True):
return func(*args, **kwargs)
acct = basic_authentication() # custom account lookup function
if acct:
return func(*args, **kwargs)
restful.abort(401)
return wrapper
class Resource(restful.Resource):
method_decorators = [authenticate] # applies to all inherited resources
```
由于 Flask-RESTful Resources 實際上是 Flask 視圖對象,你也可以使用標準的 [flask 視圖裝飾器](http://flask.pocoo.org/docs/views/#decorating-views)。
## 自定義錯誤處理器
錯誤處理是一個很棘手的問題。你的 Flask 應用可能身兼數職,然而你要以正確的內容類型以及錯誤語法處理所有的 Flask-RESTful 錯誤。
Flask-RESTful 在 Flask-RESTful 路由上發生任何一個 400 或者 500 錯誤的時候調用 [`handle_error()`](api.html#flask.ext.restful.Api.handle_error "flask.ext.restful.Api.handle_error") 函數,不會干擾到其它的路由。你可能需要你的應用程序在 404 Not Found 錯誤上返回一個攜帶正確媒體類型(介質類型)的錯誤信息;在這種情況下,使用 [`Api`](api.html#flask.ext.restful.Api "flask.ext.restful.Api") 構造函數的 `catch_all_404s` 參數。
```
app = Flask(__name__)
api = flask_restful.Api(app, catch_all_404s=True)
```
Flask-RESTful 會處理除了自己路由上的錯誤還有應用程序上所有的 404 錯誤。
有時候你想在發生錯誤的時候做一些特別的東西 - 記錄到文件,發送郵件,等等。使用 `got_request_exception()` 方法把自定義錯誤處理加入到異常。
```
def log_exception(sender, exception, **extra):
""" Log an exception to our logging framework """
sender.logger.debug('Got exception during processing: %s', exception)
from flask import got_request_exception
got_request_exception.connect(log_exception, app)
```
### 定義自定義錯誤消息
在一個請求期間遇到某些錯誤的時候,你可能想返回一個特定的消息以及/或者狀態碼。你可以告訴 Flask-RESTful 你要如何處理每一個錯誤/異常,因此你不必在你的 API 代碼中編寫 try/except 代碼塊。
```
errors = {
'UserAlreadyExistsError': {
'message': "A user with that username already exists.",
'status': 409,
},
'ResourceDoesNotExist': {
'message': "A resource with that ID no longer exists.",
'status': 410,
'extra': "Any extra information you want.",
},
}
```
包含 `‘status’` 鍵可以設置響應的狀態碼。如果沒有指定的話,默認是 500.
一旦你的 `errors` 字典定義,簡單地把它傳給 [`Api`](api.html#flask.ext.restful.Api "flask.ext.restful.Api") 構造函數。
```
app = Flask(__name__)
api = flask_restful.Api(app, errors=errors)
```
# 中高級用法
本頁涉及構建一個稍微復雜的 Flask-RESTful 應用程序,該應用程序將會覆蓋到一些最佳練習當你建立一個真實世界的基于 Flask-RESTful 的 API。[_快速入門_](quickstart.html#quickstart) 章節適用于開始你的第一個 Flask-RESTful 應用程序,因此如果你是 Flask-RESTful 的新用戶,最好查閱該章節。
## 項目結構
有許多不同的方式來組織你的 Flask-RESTful 應用程序,但是這里我們描述了一個在大型的應用程序中能夠很好地擴展并且維持一個不錯的文件組織。
最基本的思路就是把你的應用程序分為三個主要部分。路由,資源,以及任何公共的基礎部分。
下面是目錄結構的一個例子:
```
myapi/
__init__.py
app.py # this file contains your app and routes
resources/
__init__.py
foo.py # contains logic for /Foo
bar.py # contains logic for /Bar
common/
__init__.py
util.py # just some common infrastructure
```
common 文件夾可能只包含一組輔助函數以滿足你的應用程序公共的需求。例如,它也可能包含任何自定義輸入/輸出類型。
在 resource 文件夾中,你只有資源對象。因此這里就是 foo.py 可能的樣子:
```
from flask.ext import restful
class Foo(restful.Resource):
def get(self):
pass
def post(self):
pass
```
app.py 中的配置就像這樣:
```
from flask import Flask
from flask.ext import restful
from myapi.resources.foo import Foo
from myapi.resources.bar import Bar
from myapi.resources.baz import Baz
app = Flask(__name__)
api = restful.Api(app)
api.add_resource(Foo, '/Foo', '/Foo/<str:id>')
api.add_resource(Bar, '/Bar', '/Bar/<str:id>')
api.add_resource(Baz, '/Baz', '/Baz/<str:id>')
```
因為你可能編寫一個特別大型或者復雜的 API,這個文件里面會有一個所有路由以及資源的復雜列表。你也可以使用這個文件來設置任何的配置值(before_request,after_request)。基本上,這個文件配置你整個 API。
## 完整的參數解析示例
在文檔的其它地方,我們已經詳細地介紹了如何使用 reqparse 的例子。這里我們將設置一個有多個輸入參數的資源。我們將定義一個名為 “User” 的資源。
```
from flask.ext import restful
from flask.ext.restful import fields, marshal_with, reqparse
def email(email_str):
""" return True if email_str is a valid email """
if valid_email(email):
return True
else:
raise ValidationError("{} is not a valid email")
post_parser = reqparse.RequestParser()
post_parser.add_argument(
'username', dest='username',
type=str, location='form',
required=True, help='The user\'s username',
)
post_parser.add_argument(
'email', dest='email',
type=email, location='form',
required=True, help='The user\'s email',
)
post_parser.add_argument(
'user_priority', dest='user_priority',
type=int, location='form',
default=1, choices=range(5), help='The user\'s priority',
)
user_fields = {
'id': fields.Integer,
'username': fields.String,
'email': fields.String,
'user_priority': fields.Integer,
'custom_greeting': fields.FormattedString('Hey there {username}!'),
'date_created': fields.DateTime,
'date_updated': fields.DateTime,
'links': fields.Nested({
'friends': fields.Url('/Users/{id}/Friends'),
'posts': fields.Url('Users/{id}/Posts'),
}),
}
class User(restful.Resource):
@marshal_with(user_fields)
def post(self):
args = post_parser.parse_args()
user = create_user(args.username, args.email, args.user_priority)
return user
@marshal_with(user_fields)
def get(self, id):
args = get_parser.parse_args()
user = fetch_user(id)
return user
```
正如你所看到的,我們創建一個 `post_parser` 專門用來處理解析 POST 請求攜帶的參數。讓我們逐步介紹每一個定義的參數。
```
post_parser.add_argument(
'username', dest='username',
type=str, location='form',
required=True, help='The user\'s username',
)
```
`username` 字段是所有參數中最為普通的。它從 POST 數據中獲取一個字符串并且把它轉換成一個字符串類型。該參數是必須得(`required=True`),這就意味著如果不提供改參數,Flask-RESTful 會自動地返回一個消息是’用戶名字段是必須‘的 400 錯誤。
```
post_parser.add_argument(
'email', dest='email',
type=email, location='form',
required=True, help='The user\'s email',
)
```
`email` 字段是一個自定義的 `email` 類型。在最前面幾行中我們定義了一個 `email` 函數,它接受一個字符串,如果該字符串類型合法的話返回 True,否則會引起一個 `ValidationError` 異常,該異常明確表示 email 類型不合法。
```
post_parser.add_argument(
'user_priority', dest='user_priority',
type=int, location='form',
default=1, choices=range(5), help='The user\'s priority',
)
```
`user_priority` 類型充分利用了 `choices` 參數。這就意味著如果提供的 `user_priority` 值不落在由 `choices` 參數指定的范圍內的話,Flask-RESTful 會自動地以 400 狀態碼以及一個描述性的錯誤消息響應。
下面該討論到輸入了。我們也在 `user_fields` 字典中定義了一些有趣的字段類型用來展示一些特殊的類型。
```
user_fields = {
'id': fields.Integer,
'username': fields.String,
'email': fields.String,
'user_priority': fields.Integer,
'custom_greeting': fields.FormattedString('Hey there {username}!'),
'date_created': fields.DateTime,
'date_updated': fields.DateTime,
'links': fields.Nested({
'friends': fields.Url('/Users/{id}/Friends', absolute=True),
'posts': fields.Url('Users/{id}/Posts', absolute=True),
}),
}
```
首先,存在一個 `fields.FormattedString`。
```
'custom_greeting': fields.FormattedString('Hey there {username}!'),
```
此字段主要用于篡改響應中的值到其它的值。在這種情況下,`custom_greeting` 將總是包含從 `username` 字段返回的值。
下一步,檢查 `fields.Nested`。
```
'links': fields.Nested({
'friends': fields.Url('/Users/{id}/Friends', absolute=True),
'posts': fields.Url('Users/{id}/Posts', absolute=True),
}),
```
此字段是用于在響應中創建子對象。在這種情況下,我們要創建一個包含相關對象 urls 的 `links` 子對象。注意這里我們是使用了 `fields.Nested`。
最后,我們使用了 `fields.Url` 字段類型。
```
'friends': fields.Url('/Users/{id}/Friends', absolute=True),
'posts': fields.Url('Users/{id}/Posts', absolute=True),
```
它接受一個字符串作為參數,它能以我們上面提到的 `fields.FormattedString` 同樣的方式被格式化。傳入 `absolute=True` 確保生成的 Urls 包含主機名。
# API 文檔
`flask.ext.restful.marshal(data, fields, envelope=None)`
Takes raw data (in the form of a dict, list, object) and a dict of fields to output and filters the data based on those fields.
Parameters:
* **data** – the actual object(s) from which the fields are taken from
* **fields** – a dict of whose keys will make up the final serialized response output
* **envelope** – optional key that will be used to envelop the serialized response
```
>>> from flask.ext.restful import fields, marshal
>>> data = { 'a': 100, 'b': 'foo' }
>>> mfields = { 'a': fields.Raw }
```
```
>>> marshal(data, mfields)
OrderedDict([('a', 100)])
```
```
>>> marshal(data, mfields, envelope='data')
OrderedDict([('data', OrderedDict([('a', 100)]))])
```
`flask.ext.restful.marshal_with(fields, envelope=None)`
A decorator that apply marshalling to the return values of your methods.
```
>>> from flask.ext.restful import fields, marshal_with
>>> mfields = { 'a': fields.Raw }
>>> @marshal_with(mfields)
... def get():
... return { 'a': 100, 'b': 'foo' }
...
...
>>> get()
OrderedDict([('a', 100)])
```
```
>>> @marshal_with(mfields, envelope='data')
... def get():
... return { 'a': 100, 'b': 'foo' }
...
...
>>> get()
OrderedDict([('data', OrderedDict([('a', 100)]))])
```
see [`flask.ext.restful.marshal()`](#flask.ext.restful.marshal "flask.ext.restful.marshal")
`flask.ext.restful.marshal_with_field(field)`
A decorator that formats the return values of your methods with a single field.
```
>>> from flask.ext.restful import marshal_with_field, fields
>>> @marshal_with_field(fields.List(fields.Integer))
... def get():
... return ['1', 2, 3.0]
...
>>> get()
[1, 2, 3]
```
see [`flask.ext.restful.marshal_with()`](#flask.ext.restful.marshal_with "flask.ext.restful.marshal_with")
`flask.ext.restful.abort(http_status_code, **kwargs)`
Raise a HTTPException for the given http_status_code. Attach any keyword arguments to the exception for later processing.
## Api
`class flask.ext.restful.Api(app=None, prefix='', default_mediatype='application/json', decorators=None, catch_all_404s=False, url_part_order='bae', errors=None)`
The main entry point for the application. You need to initialize it with a Flask Application:
```
>>> app = Flask(__name__)
>>> api = restful.Api(app)
```
Alternatively, you can use [`init_app()`](#flask.ext.restful.Api.init_app "flask.ext.restful.Api.init_app") to set the Flask application after it has been constructed.
Parameters:
* **app** (_flask.Flask_) – the Flask application object
* **prefix** (_str_) – Prefix all routes with a value, eg v1 or 2010-04-01
* **default_mediatype** (_str_) – The default media type to return
* **decorators** (_list_) – Decorators to attach to every resource
* **catch_all_404s** (_bool_) – Use [`handle_error()`](#flask.ext.restful.Api.handle_error "flask.ext.restful.Api.handle_error") to handle 404 errors throughout your app
* **url_part_order** – A string that controls the order that the pieces of the url are concatenated when the full url is constructed. ‘b’ is the blueprint (or blueprint registration) prefix, ‘a’ is the api prefix, and ‘e’ is the path component the endpoint is added with
* **errors** () – A dictionary to define a custom response for each exception or error raised during a request
`add_resource(resource, *urls, **kwargs)`
Adds a resource to the api.
Parameters:
* **resource** ([`Resource`](#flask.ext.restful.Resource "flask.ext.restful.Resource")) – the class name of your resource
* **urls** (_str_) – one or more url routes to match for the resource, standard flask routing rules apply. Any url variables will be passed to the resource method as args.
* **endpoint** (_str_) – endpoint name (defaults to `Resource.__name__.lower()` Can be used to reference this route in [`fields.Url`](#fields.Url "fields.Url") fields
Additional keyword arguments not specified above will be passed as-is to `flask.Flask.add_url_rule()`.
Examples:
```
api.add_resource(HelloWorld, '/', '/hello')
api.add_resource(Foo, '/foo', endpoint="foo")
api.add_resource(FooSpecial, '/special/foo', endpoint="foo")
```
`error_router(original_handler, e)`
This function decides whether the error occured in a flask-restful endpoint or not. If it happened in a flask-restful endpoint, our handler will be dispatched. If it happened in an unrelated view, the app’s original error handler will be dispatched. In the event that the error occurred in a flask-restful endpoint but the local handler can’t resolve the situation, the router will fall back onto the original_handler as last resort.
Parameters:
* **original_handler** (_function_) – the original Flask error handler for the app
* **e** (_Exception_) – the exception raised while handling the request
`handle_error(e)`
Error handler for the API transforms a raised exception into a Flask response, with the appropriate HTTP status code and body.
Parameters: **e** (_Exception_) – the raised Exception object
`init_app(app)`
Initialize this class with the given `flask.Flask` application or `flask.Blueprint` object.
Parameters: **app** (_flask.Blueprint_) – the Flask application or blueprint object
Examples:
```
api = Api()
api.add_resource(...)
api.init_app(app)
```
`make_response(data, *args, **kwargs)`
Looks up the representation transformer for the requested media type, invoking the transformer to create a response object. This defaults to (application/json) if no transformer is found for the requested mediatype.
Parameters: **data** – Python object containing response data to be transformed
`mediatypes()`
Returns a list of requested mediatypes sent in the Accept header
`mediatypes_method()`
Return a method that returns a list of mediatypes
`output(resource)`
Wraps a resource (as a flask view function), for cases where the resource does not directly return a response object
Parameters: **resource** – The resource as a flask view function
`owns_endpoint(endpoint)`
Tests if an endpoint name (not path) belongs to this Api. Takes in to account the Blueprint name part of the endpoint name.
Parameters: **endpoint** – The name of the endpoint being checked
Returns: bool
`representation(mediatype)`
Allows additional representation transformers to be declared for the api. Transformers are functions that must be decorated with this method, passing the mediatype the transformer represents. Three arguments are passed to the transformer:
* The data to be represented in the response body
* The http status code
* A dictionary of headers
The transformer should convert the data appropriately for the mediatype and return a Flask response object.
Ex:
```
@api.representation('application/xml')
def xml(data, code, headers):
resp = make_response(convert_data_to_xml(data), code)
resp.headers.extend(headers)
return resp
```
`resource(*urls, **kwargs)`
Wraps a [`Resource`](#flask.ext.restful.Resource "flask.ext.restful.Resource") class, adding it to the api. Parameters are the same as [`add_resource()`](#flask.ext.restful.Api.add_resource "flask.ext.restful.Api.add_resource").
Example:
```
app = Flask(__name__)
api = restful.Api(app)
@api.resource('/foo')
class Foo(Resource):
def get(self):
return 'Hello, World!'
```
`unauthorized(response)`
Given a response, change it to ask for credentials
`url_for(resource, **values)`
Generates a URL to the given resource.
`class flask.ext.restful.Resource`
Represents an abstract RESTful resource. Concrete resources should extend from this class and expose methods for each supported HTTP method. If a resource is invoked with an unsupported HTTP method, the API will return a response with status 405 Method Not Allowed. Otherwise the appropriate method is called and passed all arguments from the url rule used when adding the resource to an Api instance. See [`add_resource()`](#flask.ext.restful.Api.add_resource "flask.ext.restful.Api.add_resource") for details.
## ReqParse
`class reqparse.RequestParser(argument_class=<class 'reqparse.Argument'>, namespace_class=<class 'reqparse.Namespace'>)`
Enables adding and parsing of multiple arguments in the context of a single request. Ex:
```
from flask import request
parser = RequestParser()
parser.add_argument('foo')
parser.add_argument('int_bar', type=int)
args = parser.parse_args()
```
`add_argument(*args, **kwargs)`
Adds an argument to be parsed.
Accepts either a single instance of Argument or arguments to be passed into [`Argument`](#reqparse.Argument "reqparse.Argument")‘s constructor.
See [`Argument`](#reqparse.Argument "reqparse.Argument")‘s constructor for documentation on the available options.
`copy()`
Creates a copy of this RequestParser with the same set of arguments
`parse_args(req=None, strict=False)`
Parse all arguments from the provided request and return the results as a Namespace
Parameters: **strict** – if req includes args not in parser, throw 400 BadRequest exception
`remove_argument(name)`
Remove the argument matching the given name.
`replace_argument(name, *args, **kwargs)`
Replace the argument matching the given name with a new version.
`class reqparse.Argument(name, default=None, dest=None, required=False, ignore=False, type=<function <lambda> at 0x1065c6c08>, location=('json', 'values'), choices=(), action='store', help=None, operators=('=', ), case_sensitive=True, store_missing=True)`
Parameters:
* **name** – Either a name or a list of option strings, e.g. foo or -f, –foo.
* **default** – The value produced if the argument is absent from the request.
* **dest** – The name of the attribute to be added to the object returned by [`parse_args()`](#reqparse.RequestParser.parse_args "reqparse.RequestParser.parse_args").
* **required** (_bool_) – Whether or not the argument may be omitted (optionals only).
* **action** – The basic type of action to be taken when this argument is encountered in the request. Valid options are “store” and “append”.
* **ignore** – Whether to ignore cases where the argument fails type conversion
* **type** – The type to which the request argument should be converted. If a type raises a ValidationError, the message in the error will be returned in the response. Defaults to `unicode` in python2 and `str` in python3.
* **location** – The attributes of the `flask.Request` object to source the arguments from (ex: headers, args, etc.), can be an iterator. The last item listed takes precedence in the result set.
* **choices** – A container of the allowable values for the argument.
* **help** – A brief description of the argument, returned in the response when the argument is invalid with the name of the argument and the message passed to a ValidationError raised by a type converter.
* **case_sensitive** (_bool_) – Whether the arguments in the request are case sensitive or not
* **store_missing** (_bool_) – Whether the arguments default value should be stored if the argument is missing from the request.
`__init__(name, default=None, dest=None, required=False, ignore=False, type=<function <lambda> at 0x1065c6c08>, location=('json', 'values'), choices=(), action='store', help=None, operators=('=', ), case_sensitive=True, store_missing=True)`
`handle_validation_error(error)`
Called when an error is raised while parsing. Aborts the request with a 400 status and an error message
Parameters: **error** – the error that was raised
`parse(request)`
Parses argument value(s) from the request, converting according to the argument’s type.
Parameters: **request** – The flask request object to parse arguments from
`source(request)`
Pulls values off the request in the provided location :param request: The flask request object to parse arguments from
## Fields
`class fields.String(default=None, attribute=None)`
Marshal a value as a string. Uses `six.text_type` so values will be converted to `unicode` in python2 and `str` in python3.
`format(value)`
`class fields.Url(endpoint=None, absolute=False, scheme=None)`
FormattedString is used to interpolate other values from the response into this field. The syntax for the source string is the same as the string `format` method from the python stdlib.
Ex:
```
fields = {
'name': fields.String,
'greeting': fields.FormattedString("Hello {name}")
}
data = {
'name': 'Doug',
}
marshal(data, fields)
```
`output(key, obj)`
`class fields.Url(endpoint=None, absolute=False, scheme=None)`
A string representation of a Url
`output(key, obj)`
`class fields.DateTime(dt_format='rfc822', **kwargs)`
Return a formatted datetime string in UTC. Supported formats are RFC 822 and ISO 8601.
Parameters: **dt_format** (_str_) – `'rfc822'` or `'iso8601'`
`format(value)`
`class fields.Float(default=None, attribute=None)`
A double as IEEE-754 double precision. ex : 3.141592653589793 3.1415926535897933e-06 3.141592653589793e+24 nan inf -inf
`format(value)`
`class fields.Integer(default=0, **kwargs)`
Field for outputting an integer value.
Parameters:
* **default** (_int_) – The default value for the field, if no value is specified.
* **attribute** – If the public facing value differs from the internal value, use this to retrieve a different attribute from the response than the publicly named value.
`format(value)`
`class fields.Arbitrary(default=None, attribute=None)`
A floating point number with an arbitrary precision
ex: 634271127864378216478362784632784678324.23432
`format(value)`
`class fields.Nested(nested, allow_null=False, **kwargs)`
Allows you to nest one set of fields inside another. See [_高級:嵌套字段_](fields.html#nested-field) for more information
Parameters:
* **nested** (_dict_) – The dictionary to nest
* **allow_null** (_bool_) – Whether to return None instead of a dictionary with null keys, if a nested dictionary has all-null keys
* **\*\*kwargs** – if `default` keyword argument is present, a nested dictionary will be marshaled as its value if nested dictionary is all-null keys (e.g. lets you return an empty JSON object instead of null)
:keyword default
`output(key, obj)`
`class fields.List(cls_or_instance, **kwargs)`
Field for marshalling lists of other fields.
See [_列表字段_](fields.html#list-field) for more information.
Parameters: **cls_or_instance** – The field type the list will contain.
`format(value)`
`output(key, data)`
`class fields.Raw(default=None, attribute=None)`
Raw provides a base field class from which others should extend. It applies no formatting by default, and should only be used in cases where data does not need to be formatted before being serialized. Fields should throw a MarshallingException in case of parsing problem.
`format(value)`
Formats a field’s value. No-op by default - field classes that modify how the value of existing object keys should be presented should override this and apply the appropriate formatting.
Parameters: **value** – The value to format
Raises MarshallingException:
In case of formatting problem
Ex:
```
class TitleCase(Raw):
def format(self, value):
return unicode(value).title()
```
`output(key, obj)`
Pulls the value for the given key from the object, applies the field’s formatting and returns the result. If the key is not found in the object, returns the default value. Field classes that create values which do not require the existence of the key in the object should override this and return the desired value.
Raises MarshallingException:
In case of formatting problem
`class fields.Boolean(default=None, attribute=None)`
Field for outputting a boolean value.
Empty collections such as `""`, `{}`, `[]`, etc. will be converted to `False`.
`format(value)`
`class fields.Fixed(decimals=5, **kwargs)`
A decimal number with a fixed precision.
`format(value)`
`fields.Price`
alias of [`Fixed`](#fields.Fixed "fields.Fixed")
## Inputs
`flask.ext.restful.inputs.url(value)`
Validate a URL.
Parameters: **value** (_string_) – The URL to validate
Returns: The URL if valid.
Raises: ValueError
`flask.ext.restful.inputs.regex`
`flask.ext.restful.inputs.date(value)`
Parse a valid looking date in the format YYYY-mm-dd
`flask.ext.restful.inputs.iso8601interval(value, argument='argument')`
Parses ISO 8601-formatted datetime intervals into tuples of datetimes.
Accepts both a single date(time) or a full interval using either start/end or start/duration notation, with the following behavior:
* Intervals are defined as inclusive start, exclusive end
* Single datetimes are translated into the interval spanning the largest resolution not specified in the input value, up to the day.
* The smallest accepted resolution is 1 second.
* All timezones are accepted as values; returned datetimes are localized to UTC. Naive inputs and date inputs will are assumed UTC.
Examples:
```
"2013-01-01" -> datetime(2013, 1, 1), datetime(2013, 1, 2)
"2013-01-01T12" -> datetime(2013, 1, 1, 12), datetime(2013, 1, 1, 13)
"2013-01-01/2013-02-28" -> datetime(2013, 1, 1), datetime(2013, 2, 28)
"2013-01-01/P3D" -> datetime(2013, 1, 1), datetime(2013, 1, 4)
"2013-01-01T12:00/PT30M" -> datetime(2013, 1, 1, 12), datetime(2013, 1, 1, 12, 30)
"2013-01-01T06:00/2013-01-01T12:00" -> datetime(2013, 1, 1, 6), datetime(2013, 1, 1, 12)
```
Parameters: **value** (_str_) – The ISO8601 date time as a string
Returns: Two UTC datetimes, the start and the end of the specified interval
Return type: A tuple (datetime, datetime)
Raises: ValueError, if the interval is invalid.
`flask.ext.restful.inputs.natural(value, argument='argument')`
Restrict input type to the natural numbers (0, 1, 2, 3...)
`flask.ext.restful.inputs.boolean(value)`
Parse the string “true” or “false” as a boolean (case insensitive). Also accepts “1” and “0” as True/False (respectively). If the input is from the request JSON body, the type is already a native python boolean, and will be passed through without further parsing.
`flask.ext.restful.inputs.rfc822(dt)`
Turn a datetime object into a formatted date.
Example:
```
inputs.rfc822(datetime(2011, 1, 1)) => "Sat, 01 Jan 2011 00:00:00 -0000"
```
Parameters: **dt** (_datetime_) – The datetime to transform
Returns: A RFC 822 formatted date string
# 運行測試
`Makefile` 文件中照顧到搭建一個 virtualenv 為運行測試。所有你需要做的就是運行:
```
$ make test
```
要更改用于運行測試的 Python 版本(默認是 Python 2.7),修改上面 `Makefile` 文件中的 `PYTHON_MAJOR` 和 `PYTHON_MINOR` 變量。
你可以在所有支持的版本上運行測試:
```
$ make test-all
```
單個的測試可以使用如下格式的命令運行:
```
nosetests <filename>:ClassName.func_name
```
例如:
```
$ source env/bin/activate
$ nosetests tests/test_reqparse.py:ReqParseTestCase.test_parse_choices_insensitive
```
另外,提交你的更改到 Github 中你的分支上,Travis 將會自動地為你的分支運行測試。
也提供了一個 Tox 配置文件,因此你可以在本地在多個 python 版本(2.6,2.7,3.3,和 3.4)上測試
```
$ tox
```