controller
ODOO中的控制器Controller,和其他框架中的控制器(有些称为路由/接口,具体看具体拿它来做什么)作为后台和前段数据交互的桥梁。但他和其他框架中的控制器又有些不一样的地方,就是在某些参数控制下,如果没有创建数据库,或者在多数据库却没有指定数据库时,控制器是不可用的。
ODOO中的控制器,也具有拓展性,控制器通过继承Controller进行创建,就像ODOO中的模型需要继承Model创建。控制器方法通过route()装饰器与路由关联,route作为修饰控制器的装饰器,它采用路由字符串和许多属性来自定义其行为或安全性。
from odoo import http
class MainControllers(http.Controller):
@http.route('/hello', auth='public', website=True)
def print_hello_world(self):
return '<h1>Hello, World!</h1>'
上面的例子是最简单的实现,在新的页签访问http://localhost:8069/hello,会看到 Hello, World!
route 装饰器的参数及意义如下:
- route – 字符串或字符串列表。决定哪种http请求匹配所装饰方法的路由部分。可以是单个字符串或字符串列表。
- type – 请求的类型,可为
'http'
或'json'
. - auth – 认证方法的类型,可为以下类型:
user
: 用户必须认证且当前请求将使用用户的权限进行执行。public
: 用户可认证也可不认证。如未认证,当前请求会使用共享Public用户进行执行。none
: 即使用没有数据库,方法也一直是活跃的。主要由框架和认证模块使用。它们的请求代码对访问数据库没有任何作用,也没有表明当前数据库或当前用户的配置。
- methods – 一个这个路由所应用的http方法的列表。如未指定,允许使用所有方法,常用的请求方式有GET、POST。
- cors – Access-Control-Allow-Origin cors 指令值,主要用来控制解决跨域问题。
- csrf (
bool
) – 是否应为路由启用CSRF保护。默认为True
。
route 参数
像上面最简单的例子,它的route参数是完全匹配URL的。另外,路由字符串也可以使用转换器模式来匹配URL,并使这些位作为本地变量可用。例如,我们可以创建一个新的控制器方法,它接受一个动态的参数,并将此参数作为本地变量输出:
... ...
@http.route('/hello/<name>', auth='public', website=True)
def print_hello_name(self, name):
return '<h1>Hello, {}!</h1>'.format(name)
在新的页签访问http://localhost:8069/hello/ZERONE,会看到 Hello, ZERONE!注意看控制器方法,它额外接收了一个name参数。可以直接作为变量使用,参数还可以使用另一种方式接收(看下面的例子),使用可变参数。但这么做的前提是修饰起中的 type 参数应为 http。上面的例子中,没有书写此参数,因为此参数默认为 http 。
... ...
@http.route('/hello/<name>/<greetings>', auth='public', website=True)
def print_greetings(self, **kw):
return '<h1>Hello, {}! {} !</h1>'.format(kw.get("name"),kw.get("greetings"))
在新的页签访问http://localhost:8069/hello/ZERONE/傍晚好!,会看到 Hello, ZERONE!傍晚好! 在这个例子中,使用可变参数也能获取并做为变量使用。写到这里,还要说一下,在同一个class中,不要书写相同的控制器名称,即使URLs相同也不行。还有一点,接收的参数名需要和URLs里占位符的参数名相同。
Odoo还额外提供了一个的转换器,称为model,它可以在给定id时直接提供记录。让我们在zerone.book模型中增加一条name为 来学ODOO13吧 的新记录,并记下它的id(假如新建记录的id=1),然后增加如下代码:
... ...
@http.route('/find/book/<model("zerone.book"):book>/', auth='public', website=True)
def find_a_book_by_id(self, book):
return book.name
在新的页签访问http://localhost:8069/find/book/1/,会看到页面输出 来学ODOO13吧 。在这个例子中,我们在route参数中,使用<model("zerone.book"):book>指定了模型为 zerone.book,它也代表了我们传入的参数id,并把zerone.book 模型中,id 为1 的记录用 book 来表示。
methods 参数
上面的例子,都是默认使用 type = http 的控制器,当 type = http 的时候,可以使用 methods 参数控制请求方式。在最上面的参数说明中有提到:如果不显示的限制请求方式,这将允许所有的请求方式。
... ...
@http.route('/book_list', type="http", auth='public', website=True, cors='*', csrf=False)
def book_list(self, **kw):
print(kw)
book_obj = http.request.env['zerone.book']
books = book_obj.sudo().search([], order='id desc')
book_lst = []
for book in books:
book_lst.append({"id":book.id,"name":book.name})
return json.dumps({"book_lst":book_lst})
除了在通过控制器方法中的固定参数、动态参数(**kw)接收请求参数外,还可以在控制器方法中不使用任何形式的参数,把参数解析放到控制器方法体中,使用 http.request.httprequest.args 获取参数,就像下面这样:
... ...
@http.route('/book_list', type="http", auth='public', website=True, cors='*', csrf=False)
def book_list(self):
kw = http.request.httprequest.args
print(kw.get("a"))
book_obj = http.request.env['zerone.book']
books = book_obj.sudo().search([], order='id desc')
book_lst = []
for book in books:
book_lst.append({"id":book.id,"name":book.name})
return json.dumps({"book_lst":book_lst})
我们可以通过GET方式请求上面的接口(在浏览器直接请求就可见效果),也可以使用POST方式请求此借口,下面使用PostMan或者其他工具演示例子(使用工具测试,可能会引起跨域问题,和csrf安全保护问题,所以在修饰起里需要加上csrf = False 和 cors = "*")。在做请求时,你需要按下面的操作,保证你的借口可被访问到:
- 你需要将你的控制器所在模块,配置到config文件的server_wide_modules参数中
- 如果你有多个数据库,你需要将要测试的数据库名,配置到config文件的dbfilter参数中
就像这样
[options]
db_host = localhost
db_port = 5432
... ...
server_wide_modules = web,zerone_inherit
dbfilter = ^db_demo$
配置好上面两条后,可以进行请求测试了,先使用GET方式请求:
再使用POST方式请求:
通过了上面两个借口测试,现在试着通过增加 methods = ["GET"] 控制只允许GET请求,并通过POST方式请求此借口:
@http.route('/book_list/get', type="http", methods=["GET"], auth='public', website=True, cors='*', csrf=False)
def book_list_only_get(self, **kw):
print(kw)
book_obj = http.request.env['zerone.book']
books = book_obj.sudo().search([], order='id desc')
book_lst = []
for book in books:
book_lst.append({"id":book.id,"name":book.name})
return json.dumps({"book_lst":book_lst})
我们将得到如下信息,请求方式不允许:
通过增加 methods = ["POST"] 控制只允许POST请求,并通过GET方式请求此借口:
@http.route('/book_list/post', type="http", methods=["POST"], auth='public', website=True, cors='*', csrf=False)
def book_list_only_post(self, **kw):
print(kw)
book_obj = http.request.env['zerone.book']
books = book_obj.sudo().search([], order='id desc')
book_lst = []
for book in books:
book_lst.append({"id":book.id,"name":book.name})
return json.dumps({"book_lst":book_lst})
同样的,我们也将得到如下信息,请求方式不允许:
以上就是在 type = http 时,使用 methods 参数的作用。
type 参数
上面的例子都是请求类型(type)为 http 的控制器,除了http,还有一种类型为 json。下面将通过例子说明 json 类型的控制器的书写方式。
像下面这样,使用 type="json" ,在控制器方法体中使用 http.request.jsonrequest 来获取参数(注意使用json时,方法参数使用固定参数和动态参数,是获取不到数据的):
... ...
@http.route('/book_list/json', type="json", auth='public', website=True, cors='*', csrf=False)
def book_list_json(self):
kw = http.request.jsonrequest
print(kw)
book_obj = http.request.env['zerone.book']
books = book_obj.sudo().search([], order='id desc')
book_lst = []
for book in books:
book_lst.append({"id":book.id,"name":book.name})
return {"book_lst":book_lst}
可以看到,json类型的路由返回值是有固定格式的,控制器返回的结果被作为result的值封装起来。如果你是用json类型的请求,发现返回值多了这么个结构,不要惊讶,也别想着怎么去改它,你能做的就是适应它。感兴趣可以学习一下 json-rpc。
接口调试,由基友LiuC来补充,等待更新