zoukankan      html  css  js  c++  java
  • 【odoo14】【好书学习】第十三章、网站开发(对外服务)

    老韩头的开发日常【好书学习】系列

    本章我们将介绍一些关于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。
      在本节中,我们在页面上显示了一个静态资源(图像),并看到了不同静态资源的推荐目录。还有更简单的方法来表示页面内容和静态资源,我们将在下一章中看到。

    本文来自博客园,作者:老韩头的开发日常,转载请注明原文链接:https://www.cnblogs.com/xushuotec/p/14493393.html

  • 相关阅读:
    mysql操作
    Linux内核事件通知链学习
    C++双端队列学习
    tune的结果图是什么
    conda环境备份
    Could not load dynamic library 'libcudart.so.11.0';
    Unexpected error while saving file: xxx.ipynb database or disk is full
    友元函数与友元类
    构造函数初始化必须采用初始化列表的情况
    模型集成04-GMM
  • 原文地址:https://www.cnblogs.com/xushuotec/p/14493393.html
Copyright © 2011-2022 走看看