本章我们将介绍一些关于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. Chapter Zero 0.1.2 CPU的架构

    CPU的架构 CPU内部含有一些微指令, 我们所使用的软件都要经过CPU内部的微指令集达成才行. 这些指令集的设计又分为两种设计理念, 这就是目前世界上常见的两种主要CPU架构: 精简指令集(Redu ...

  2. C# 数据类型(3)

    动态类型 dynamic types 动态类型是后来引进的,他其实是一个static type,但是不像其他的静态类型,编译器不会检查你到底是啥类型(也不会检查你能不能去call某个'method') ...

  3. 美团CodeM资格赛第二题

    锦标赛 时间限制:1秒 空间限制:32768K 组委会正在为美团点评CodeM大赛的决赛设计新赛制.比赛有 n 个人参加(其中 n 为2的幂),每个参赛者根据资格赛和预赛.复赛的成绩,会有不同的积分. ...

  4. python类属性和对象属性、类的普通方法和静态方法

    类属性和对象属性的定义 class LearnClass(): #类属性 cls_attr = None def __init__(self,arg): #对象属性 self.obj_attr = a ...

  5. Taro 3.x in Action

    Taro 3.x in Action React, 小程序 https://taro-docs.jd.com/taro/docs/README Taro Next 跨端, 跨框架 Taro 是一个开放 ...

  6. JavaScript 注释规范

    JavaScript 注释规范 总原则 As short as possible(如无必要,勿增注释).尽量提高代码本身的清晰性.可读性. As long as necessary(如有必要,尽量详尽 ...

  7. Flow All In One

    Flow All In One Flow is a static type checker for JavaScript https://github.com/facebook/flow https: ...

  8. Learning Web Performance with MDN

    Learning Web Performance with MDN Web 性能是客观的衡量标准,是加载时间和运行时的感知用户体验. https://developer.mozilla.org/en- ...

  9. Interview Questions All In One

    Interview Questions All In One web fullstack System Design Operating System Object-Oriented Design O ...

  10. flutter 混合开发

    flutter 混合开发 https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps https://flutter.de ...