本章我们将介绍一些关于odoo web服务方面的基础知识。进阶的内容,将在第十四章介绍。

odoo中的web请求是由python的werkzeug库驱动的。odoo为了操作方便,对werkzeug进行了封装。

本章,将包含如下内容:

  • 配置url路径
  • 为url配置访问控制
  • 处理请求内容
  • 继承url的处理函数
  • 提供静态资源

配置url路径

准备

我们想所有的用户都可以获取所有的图书列表。此外,我们希望通过JSON请求向程序提供相同的信息。

步骤

  1. 添加controller/main.py如下:
from odoo import http
from odoo.http import request
class Main(http.Controller): @http.route('/my_library/books', type='http',auth='none')
def books(self):
books = request.env['library.book'].sudo().search([])
html_result = '<html><body><ul>'
for book in books:
html_result += "<li> %s </li>" % book.name
html_result += '</ul></body></html>'
return html_result
  1. 添加一个函数以JSON格式提供相同的信息,示例如下:
@http.route('/my_library/books/json', type='json', auth='none')
def books_json(self):
records = request.env['library.book'].sudo().search([])
return records.read(['name'])
  1. 添加controllers/init.py
from . import main
  1. 引入controller
from . import controllers

重启odoo服务后,我们可通过/my_library/books url访问页面,将展示图书列表。为了测试JSON-RPC部分,可通过如下:

curl -i -X POST -H "Content-Type: application/json" -d "{}" localhost:8069/my_library/books/json

如果报错(404),可能是因为我们有多个数据库。odoo并不知道需要使用哪个数据库。

可通过--db-filter='^yourdatabasename$'启动odoo实例。

原理

两个核心的要点,controller,衍生自odoo.http.Controller;方法,衍生自odoo.http.route。controller将注册到odoo的路由系统中,与模型的注册方式类似。

通常,由你的附加组件处理的路径应该以你的附加组件的名称开始,以避免名称冲突。当然,如果您扩展了该附加组件的一些功能,您将使用该附加组件的名称。

odoo.http.route

route装饰器将标识该方法是允许被web访问的,第一个参数是访问的地址。除了字符串形式,第一个参数也可以的字符串的列表,这可以使用同一个函数处理多个url的请求。

type参数默认是http,这将决定请求的支持的模式。严格来说,JSON也是HTTP,将type='json'将会让工作变的容易很多,因为odoo将会帮我们解决类型转化的问题。

auth变量将在随后的章节中介绍。

返回值

请求的返回值是由type参数决定的。对于type='http'的函数,通常返回HTML,所以第一个函数返回了HTML的字符串。另一种方式是通过request.make_response(),我们可以自定义返回header和body。因此,为了指明页面最后一次更新的时间,我们可以将books()中的最后一行更改为以下代码:

return request.make_response(
html_result, headers=[
('Last-modified', email.utils.formatdate((fields.Datetime.from_string(request.env['library.book'].sudo().search([], order='write_date desc', limit=1).write_date) -datetime.datetime(1970, 1, 1)).total_seconds(),usegmt=True)),
])

这段代码发送一个last -modified头和我们生成的HTML,告诉浏览器列表最后一次修改的时间。我们可以从库的write_date字段中提取这一信息。本模型。

为了让上面的代码片段工作,你必须在文件的顶部添加一些导入,如下所示:

import email
import datetime
from odoo import fields

您还可以手动创建werkzeug的响应对象并返回该对象,但是这样做效率比较低。

重要信息

为了演示的目的,手动生成HTML很好,但是在生产代码中绝不应该这样做,应该始终使用模板request.render()。

这将为你提供免费的本地化服务,并通过将业务逻辑与表示层分离使你的代码变得更好。此外,模板还提供了在输出HTML之前转义数据的函数。前面的代码容易受到跨站点脚本攻击(例如,如果用户设法将脚本标记插入到图书名中)。

对于JSON请求,只需返回您想要移交给客户端的数据结构;Odoo负责序列化。要实现这一点,您应该限制自己使用JSON可序列化的数据类型,这通常意味着字典、列表、字符串、浮点数和整数。

odoo.http.request

请求对象是一个静态对象,引用当前处理的请求,其中包含采取操作所需的一切。这里最重要的方面是request.env,它包含一个与self.env相同的环境对象。这个环境绑定到当前用户,而在前面的示例中并没有,因为我们使用了auth='none'。缺少用户也是为什么我们必须在示例代码中sudo()所有对模型方法的调用。

如果你习惯web开发,您将会面对会话处理。通过request.session,它是OpenERPSession对象(werkzeug的session的简单封装),和request.session.sid访问session的ID。要存储会话值,只需处理请求。会话作为字典,示例如下:

request.session['hello'] = 'world'
request.session.get('hello')

小贴士

注意,在会话中存储数据与使用全局变量没有什么不同。只有在必要的时候才使用它。这通常是多请求操作的情况,例如website_sale模块中的签出。

更多

route装饰器有额外的一些参数用于定制化请求行为。默认,所有的http请求都是可以的。methods参数,用于指定接收的请求类型,一般是['GET']或['POST']。

通过设置cors参数为"*",以允许跨域请求(处于安全考虑,浏览器将屏蔽跨域的AJAX以及其他的一些网络请求)。如果cors未设置,Access-Control-Allow-Origin头部将不会设置,浏览器将默认不允许跨域请求。在我们的例子中,我们将在/my_module/books/json设置cors参数,以允许所有的请求都可以访问该URL。

默认,odoo通过在请求中携带token保护请求免收跨站点伪造请求。如果想关掉该功能,可将csrf设置为False。

参考

关于HTTP路由的相关内容参考如下

  • 如果你在一个odoo实例中管理多个数据库,不同的数据库可能有不同的域。因此,你可以使用--db-filter,或者dbfilter_from_header模块,可帮助我们基于域过滤数据库。
  • 如何通过templates实现模块化,可通过 在后续章节中了解。

为url配置访问控制

本节,我们将探索三种权限验证。

准备

我们将利用第四章中的library.book模型。

步骤

定义controllers/main.py

  1. 添加显示所有图书的路由
@http.route('/my_library/all-books', type='http', auth='none')
def all_books(self):
books = request.env['library.book'].sudo().search([])
html_result = '<html><body><ul>'
for book in books:
html_result += "<li> %s </li>" % book.name
html_result += '</ul></body></html>'
return html_result
  1. 添加路由显示所有的图书以及被当前用户修改的图书。
@http.route('/my_library/all-books/mark-mine', type='http', auth='public')
def all_books_mark_mine(self):
books = request.env['library.book'].sudo().search([])
html_result = '<html><body><ul>'
for book in books:
if request.env.user.partner_id.id in book.author_ ids.ids:
html_result += "<li> <b>%s</b> </li>" % book.name
else:
html_result += "<li> %s </li>" % book.name
html_result += '</ul></body></html>'
return html_result
  1. 添加路由显示当前用户的图书
@http.route('/my_library/all-books/mine', type='http', auth='user')
def all_books_mine(self):
books = request.env['library.book'].search([ ('author_ids', 'in', request.env.user.partner_id.ids), ])
html_result = '<html><body><ul>'
for book in books:
html_result += "<li> %s </li>" % book.name
html_result += '</ul></body></html>'
return html_result

"/my_libaray/all-books"和"/my_library/all-books/mark-mine"路由对于未授权用户而言是一样的,而登录用户在后面的路径上看到他们的书以粗体显示。"/my_library/all-books/mine"对于未授权用户是不可见的。如果您试图在没有经过身份验证的情况下访问它,您将被重定向到登录页面。

原理

不同权限验证方法之间的不同可在request.env.user中的上下文体现出来。

对于auth='none',用户记录总是空的,即便授权用户访问该URL。如果您希望提供不依赖于用户的内容,或者希望在服务器范围的模块中提供与数据库无关的功能,可以使用此选项。

对于auth='public',对于未授权用户,用户记录将被设置为base.public_user的XML ID,授权用户将设置为自己的ID。如果你计划为授权用户及未授权用户提供服务,只是授权用户将获得额外的内容时,可以使用此选项。

对于auth='user',可确保仅授权用户可访问。request.env.user将指向系统中的用户。

更多

身份验证方法定义在ir.http模型中。无论你在auth中传递何值,odoo将ir.http模型中查找_auth_method_方法,所以你可以通过继承ir.http模型自定义身份验证方法。

例如,我们提供了一个名为base_group_user的身份验证方法,该方法将仅运行登录账户属于base.group_user权限组的时候才对URL可见。

from odoo import exceptions, http, models
from odoo.http import request class IrHttp(models.Model):
_inherit = 'ir.http' def _auth_method_base_group_user(self):
self._auth_method_user()
if not request.env.user.has_group('base.group_user'):
raise exceptions.AccessDenied()

现在,我们可以在装饰器中使用auth='base_group_user'以确保只有base.group_user的用户可以访问该URL。With a little trickery, you can extend this to auth='groups(xmlid1,...)'; its implementation is left as an exercise to the reader but is included in the GitHub repository example code at Chapter13/ r2_paths_auth/my_library/models/sample_auth_http.py.

使用路由中传递的参数

本节将探讨几种针对用户输入进行响应的方式。

步骤

首先,我们添加一个接收图书ID并展示相应图书详细内容的路由。然后,我们将参数写入路由中。

  1. 添加接收图书ID的路由
@http.route('/my_library/book_details', type='http', auth='none')
def book_details(self, book_id):
record = request.env['library.book'].sudo().browse(int(book_id))
return u'<html><body><h1>%s</h1>Authors: %s' % (record.name, u', '.join(record.author_ids.mapped('name')) or 'none',)
  1. 添加接收图书ID作为URL一部分的路由
@http.route("/my_library/book_details/<model('library. book'):book>",type='http', auth='none')
def book_details_in_path(self, book):
return self.book_details(book.id)

如果我们浏览/my_library/book_details?book_id=1,你可以看到id=1的图书的详细页面。如果id不存在,将会报错。

第二个路由是允许我们通过/my_library/book_details/1展示id=1的详细页面。

原理

默认,odoo是混用GET和POST模式的。所以,我们定义了一个路由,其中包含Book_id的参数,那么该路由是支持GET(变量位于url中)或者POST(通过form提交)的。如果我们未传递该变量,且未设置该变量的默认值,那么将会报错。

第二个例子中,我们利用在werkzeug环境下,大部分路由都是虚拟的的现实情况。因此,我们可以方便在路由中包含变量。

更多

在路径中定义参数是由werkzeug提供的一种称为转换器的功能。模型转换器是由Odoo添加的,Odoo还定义了转换器模型,这些模型接受以逗号分隔的id列表,并将包含这些id的记录集传递给处理程序。

转换器的美妙之处在于运行时将参数强制转换为期望的类型,而您可以自己使用普通的关键字参数。它们是作为字符串传递的,您必须自己处理必要的类型转换,如第一个示例所示。

内置的werkzeug转换器包括int、float和string,但也包括更复杂的,如path、any和uuid。你可以在https:// werkzeug.palletsprojects.com/en/1.0.x/上查找它们的语义。

参考

如果您想了解更多关于HTTP路由的信息,请参考以下几点:

  • Odoo的自定义转换器定义在基本模块的ir_http.py中,并在ir.http的_get_converters类方法中注册。作为练习,您可以创建自己的转换器,它允许您访问/my_library/ book_details/Odoo+cookbook页面来接收该书的详细信息(如果您之前已将其添加到库中)。
  • 如果你想了解更多关于路径上的表单提交,请参考CMS网站开发第14章的“从用户获取输入”。

继承url的处理函数

当你安装website模块时,/website/info路径会显示你的Odoo实例的一些信息。在这个配方中,我们将重写它,以更改该信息页面的布局,并更改显示的内容。

准备

安装website模块

步骤

我们必须调整现有的模板并重写现有的处理程序。我们可以这样做:

  1. 重写qweb模板, views/templates.xml
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<template id="show_website_info" inherit_id="website.show_website_info">
<xpath expr="//dl[@t-foreach='apps']" position="replace">
<table class="table">
<tr t-foreach="apps" t-as="app">
<th>
<a t-att-href="app.website">
<t t-esc="app.name" />
</a>
</th>
<td>
<t t-esc="app.summary" />
</td>
</tr>
</table>
</xpath>
</template>
</odoo>
  1. 重写路由, controllers/main.py
from odoo import http
from odoo.addons.website.controllers.main import Website class WebsiteInfo(Website):
@http.route()
def website_info(self):
result = super(WebsiteInfo, self).website_info()
result.qcontext['apps'] = result.qcontext['apps'].filtered(lambda x: x.name != 'website')
return result

现在,访问信息页面,我们仅能看到已安装的应用列表。

原理

步骤1,我们重写了QWeb模板,为了明确目标模板,需查阅源码。通常如下代码标识我们需要改写的模板名称:

return request.render('template.name', values)

在本案例中,处理程序使用的是名为website_info的模板,但它又被名为website.show_website_info的模板继承并改写,因此需要改写这个模板。我们替换了展示已安装应用的列表。对于QWeb的继承原理,可查阅第十五章,客户端开发。

为了重写处理程序,我们必须找到名为odoo.addons.website.controllers.main.website的处理程序。通过引入该类并继承实现改写。现在,通过改写我们将返回给内容进行了调整。注意,为简洁起见,此处重写的处理程序返回的是一个响应对象,而不是前面的方法所返回的HTML字符串。这个对象包含了对要使用的模板的引用以及模板可以访问的值,但是它只在请求的最后才被求值。

通常,有三种方法可以更改现有的处理程序:

  • 如果它使用QWeb模板,最简单的更改方法是重写模板。对于布局更改和小的逻辑更改,这是正确的选择。
  • QWeb模板获得传递的上下文,该上下文作为qcontext成员在响应中可用。这通常是一个可以根据需要添加或删除值的字典。在前面的例子中,我们只过滤了网站上的应用程序列表。
  • 如果处理程序接收到参数,您也可以对这些参数进行预处理,以使覆盖的处理程序按照您想要的方式运行。

更多

如前一节所述,控制器继承与模型继承的工作原理略有不同;实际上,您需要一个基类的引用,并在其上使用Python继承。

不要忘记用@http.route装饰新处理程序;Odoo使用它作为标记,将其方法暴露给网络层。如果忽略了装饰器,实际上会使处理程序的路径不可访问。

@http.route装饰器本身的行为类似于字段声明:你没有设置的每个值都将从你要重写的函数的装饰器派生,所以我们不必重复我们不想更改的值。

从你覆盖的函数接收到一个响应对象后,你可以做更多的事情,而不仅仅是改变QWeb上下文:

  • 您可以通过操作response.headers来添加或删除HTTP标头。
  • 如果您想呈现一个完全不同的模板,您可以覆盖response.template。
  • 要首先检测响应是否基于QWeb,请查询

    response.is_qweb。
  • 通过调用response.render()可以获得生成的HTML代码。

提供静态资源

Web页面包含几种类型的静态资源,比如图像、视频、CSS等等。在本教程中,我们将了解如何为模块管理这样的静态资源。

准备

对于这个配方,我们将在页面上显示一个图像。所以,准备一张图片。另外,从之前的章节中获取my_library模块。

步骤

按照以下步骤在页面上显示图像:

  1. 将图像添加到/my_library/static/src/img目录。
  2. 在controller中定义新的路由。在代码中,将图像URL替换为你的图像URL:
@http.route('/demo_page', type='http', auth='none')
def books(self):
image_url = '/my_library/static/src/image/odoo.png'
html_result = """<html>
<body>
<img src="%s"/>
</body>
</html>""" % image_url
return html_result

重新启动服务器并更新模块以应用更改。现在访问/demo_page查看页面上的图像。

原理

位于/static文件夹下的所有文件都被认为是静态资源,可以公开访问。在我们的例子中,我们将图像放在/static/src/img目录中。你可以把静态资源放在静态目录下的任何位置,但是根据文件类型有一个推荐的目录结构:

  • /static/src/img是用于存放图像的目录。
  • /static/src/css是css文件的目录。
  • /static/src/scss是存放scss文件的目录。
  • /static/src/fonts是用于字体文件的目录。
  • /static/src/js是JavaScript文件的目录。
  • /static/src/xml是客户端QWeb模板的xml文件目录。
  • /static/lib是用于存放外部库文件的目录。

    在我们的示例中,我们在页面上显示了一个图像。您还可以访问该映像

    直接从/my_library/static/src/image/ odoo.png。

    在本节中,我们在页面上显示了一个静态资源(图像),并看到了不同静态资源的推荐目录。还有更简单的方法来表示页面内容和静态资源,我们将在下一章中看到。

【odoo14】第十三章、网站开发(对外服务)的更多相关文章

  1. 项目二:企业级java电商网站开发(服务端)

    声明:项目源于网络,支持正版教程,学习使用,仅记录在此 项目介绍 企业级java电商网站开发(服务端),模块划分:用户管理,商品管理,商品品类管理,订单管理,订单详情管理,购物车管理,收货地址管理,支 ...

  2. 从0开始独立完成企业级Java电商网站开发(服务端)

    数据表结构设计 唯一索引unique,保证数据唯一性 CREATE TABLE `mmall_user` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT ...

  3. 第十三章 Odoo 12开发之创建网站前端功能

    Odoo 起初是一个后台系统,但很快就有了前端界面的需求.早期基于后台界面的门户界面不够灵活并且对移动端不友好.为解决这一问题,Odoo 引入了新的网站功能,为系统添加了 CMS(Content Ma ...

  4. 【odoo14】第十四章、CMS网站开发

    第十四章.CMS网站开发** Odoo有一个功能齐全的内容管理系统(CMS).通过拖放功能,你的最终用户可以在几分钟内设计一个页面,但是在Odoo CMS中开发一个新功能或构建块就不是那么简单了.在本 ...

  5. 第零章 HTML启蒙知识与网站开发流程

    Web前端启蒙知识:1.软件架构模式a)B/S架构:Browser-Server 浏览器服务器模型b)C/S架构:Client-Server 客户端服务器模型注1:浏览器是运行网页的应用程序注2:B/ ...

  6. 网站开发进阶(四十三)html中,路径前加“/” 与不加“/”的区别

    网站开发进阶(四十三)html中,路径前加"/" 与不加"/"的区别 前言 <script src="js/downloadify.js&quo ...

  7. 《Introduction to Tornado》中文翻译计划——第五章:异步Web服务

    http://www.pythoner.com/294.html 本文为<Introduction to Tornado>中文翻译,将在https://github.com/alioth3 ...

  8. CentOS7安装CDH 第十三章:CDH资源池配置

    相关文章链接 CentOS7安装CDH 第一章:CentOS7系统安装 CentOS7安装CDH 第二章:CentOS7各个软件安装和启动 CentOS7安装CDH 第三章:CDH中的问题和解决方法 ...

  9. PureStudy:学科知识分享——个人网站开发全解

    PureStudy:学科知识分享--个人网站开发全解 项目描述 PureStudy,学科知识分享网站. 学生可以使用这个网站,来浏览相应学科的知识点.学习总结,获取相关的资料.此外,他们可以选择上传文 ...

随机推荐

  1. python 迭代器 iter多次消费

    问题 Python 中的迭代器是我们经常使用的迭代工具, 但其只能消费一次,再次消费便会出现 StopIteration 报错. 解决方案 封装了一个类,当迭代器使用完后再次初始化. 代码 class ...

  2. Python_小程序

    一.开发前的准备工作 1.申请AppID:一个账号对应一个小程序,个人/个体只能申请5个小程序 2.下载开发工具 二.小程序的文件结构 三. 1.数据绑定 1.1数据的设置 Page( data:{ ...

  3. Rsyncd 同步服务

    目录 数据备份的策略 三种数据备份 三种数据备份的比较(转载) 不同数据备份类型组合说明(转载) Rsyncd 服务传输模式(remote synchronizetion deamon) 本地传输模式 ...

  4. kubernetes实战-交付dubbo服务到k8s集群(二)交付jenkins到k8s集群

    首先下载jenkins镜像并上传到我们自己的私有仓库:7-200 # docker pull jenkins/jenkins:2.190.3 # docker tag 22b8b9a84dbe har ...

  5. HDU - 4462 Scaring the Birds

    It's harvest season now! Farmer John plants a lot of corn. There are many birds living around his co ...

  6. redis键过期时间

    redis服务器中每个数据库都是一个redisDb,而redisDb实质上是一个字典的模型,数据库的每一个键都是一个字典的键值,对数据库的增删改查也就是对字典对象的增删改查. redis在维护带有过期 ...

  7. ituring 挂了

    ituring 挂了 图灵社区 挂了 运行时错误 "/"应用程序中的服务器错误. 运行时错误 说明: 服务器上出现应用程序错误.此应用程序的当前自定义错误设置禁止远程查看应用程序错 ...

  8. Netty & websockets

    Netty & websockets Netty is a non-blocking I/O client-server framework for the development of Ja ...

  9. Chrome V8 系统架构

    Chrome V8 系统架构 Chromium 多进程多线程架构 design-documents https://www.chromium.org/developers/design-documen ...

  10. Array in Depth

    Array in Depth Array.concat() & Array.push() https://developer.mozilla.org/en-US/docs/Web/JavaSc ...