第十四章、CMS网站开发**
Odoo有一个功能齐全的内容管理系统(CMS)。通过拖放功能,你的最终用户可以在几分钟内设计一个页面,但是在Odoo CMS中开发一个新功能或构建块就不是那么简单了。在本章中,您将探索Odoo的前台开发。您将学习如何创建网页。您还将学习如何创建用户可以在页面上拖放的构建块。进阶内容,如Urchin跟踪模块(UTMs),搜索引擎优化(SEO),多网站,GeoIP,和网站地图也涵盖在这一章。简而言之,您将了解开发交互式网站所需的所有内容。
重要信息
所有的Odoo CMS功能都是通过website和web_editor模块实现的。如果您想了解CMS在内部是如何工作的,请查看这两个模块。你可以在这里找到代码在行动视频:http://bit.ly/2UH0eMM。
本章将涵盖如下内容:
现代网站包含了大量的JavaScript和CSS文件。当页面加载到浏览器中时,这些静态文件向服务器发出单独的请求。请求次数越多,网站速度越慢。为了避免这个问题,大多数网站通过组合多个文件来提供静态资产。市场上有一些工具可以管理这类东西,但是Odoo有自己的实现来管理静态资产。
在Odoo中,静态资产管理并不像在其他应用中那么简单。Odoo有很多不同的应用程序和代码库。不同的Odoo应用程序有不同的用途和ui。这些应用程序不共享公共代码,所以在某些情况下,我们想加载一些资产,但我们不想对所有情况都这样做。在页面上加载不必要的静态资产不是一个好做法。为了避免在所有应用程序中加载额外的资源,Odoo使用了资源包的概念。资产包的工作是将所有JavaScript和CSS组合在一个文件中,并通过最小化它来减少其大小。Odoo代码库中有资产包,不同的代码库也有不同的资产包。
以下是Odoo中使用的不同资产包:
重要信息
还有一些其他用于特定应用的资产包:point_ of_sale.assets, survey.survey_assets, mass_mailing. layout, and website_slides.slide_embed_assets
Odoo通过AssetBundle类来管理它的静态资产
,它位于/odoo/addons/base/models/assetsbundle.py。现在,AssetBundle不仅可以组合多个文件;它还有更多的功能。以下是它提供的特性列表:
正如我们所看到的,Odoo针对不同的代码库有不同的资产。要获得正确的结果,您需要选择正确的资产包,将定制的JavaScript和CSS文件放入其中。例如,如果你正在设计一个网站,你需要把你的文件加载到web.assets_frontend。虽然这种情况很少见,但有时您需要创建一个全新的资产包。您可以创建自己的资产包,我们将在下一节中进行描述。
创建QWeb模板并添加JavaScript、CSS或SCSS文件,如下所示:
使用t-call-assets在QWeb模板中,你想加载这个包,如下:
…
…
步骤1,我们使用my_custom_assets外部ID创建了新的QWeb模板。在这个模板中,您需要列出所有的CSS、SCSS和JavaScript文件。首先,Odoo会将SCSS文件编译成CSS,然后将所有CSS和JavaScript文件合并成一个单独的CSS和JavaScript文件。
步骤2,我们已经在模板中加载了CSS和JavaScript资源。t-css和t-js属性只用于加载样式表或脚本。
重要信息
在大多数网站开发中,您需要将JavaScript和CSS文件添加到现有的资产包中。添加新的资产包是非常罕见的。只有当你想开发没有Odoo CMS功能的页面/应用程序时才需要它。在下一个菜谱中,您将学习如何将自定义CSS/JavaScript添加到现有的资产包中。
在Odoo中调试JavaScript非常困难,因为AssetBundle会将多个JavaScript文件合并到一个文件中,并将其最小化。通过使用资产启用developer模式,您可以跳过资产绑定,页面将单独加载静态资产,这样您就可以轻松调试。
组合资产生成一次并存储在ir中。附件的模型。在那之后,它们从附件中被送达。如果你想重新生成资产,你可以通过调试选项,如下图所示:
小贴士
正如你所知,odoo只会产生一次资产。这是一个头痛问题,因为它需要频繁重启服务器。为了解决这个问题,您可以在命令行中使用dev=xml,这将直接加载资产,因此不需要重新启动服务器。
在本节,我们将介绍如何向网站添加CSS和JavaScript。
我们使用第三章,创建odoo模块,中的my_library模块。你可以从 [GitHub](https://github. com/PacktPublishing/Odoo-14-Development-Cookbook-Fourth- Edition/tree/master/Chapter14/00_initial_module/my_library) 下载。我们将添加CSS、SCSS和JavaScript文件,这些文件将修改网站。因为我们正在修改网站,我们将需要添加网站作为依赖。像这样修改清单文件:
...
'depends': ['base', 'website'],
...
添加一个名为views/templates.xml的文件,并添加一个空视图覆盖,如以下(不要忘记在__manifest__.py中列出文件):
添加CSS和SCSS文件的引用,如下所示:
添加一个引用到你的JavaScript文件,如下所示:
在静态/src/ CSS /my_library中添加一些CSS代码。css,如下:
body main {
background: #b9ced8;
}
在静态/src/ SCSS /my_library中添加一些SCSS代码。scss,如下所示:
$my-bg-color: #1C2529;
$my-text-color: #D3F4FF;
nav.navbar {
background-color: $my-bg-color !important;
.navbar-nav .nav-link span {
color: darken($my-text-color, 15);
font-weight: 600;
}
}
footer.o_footer {
background-color: $my-bg-color !important;
color: $my-text-color;
}
在static/src/js/my_library.js中添加一些JavaScript代码,如下所示:
odoo.define('my_library', function (require) {
var core = require('web.core');
alert(core._t('Hello world'));
return {
// if you created functionality to export, add ithere
}
});
更新你的模块后,你应该看到Odoo网站在菜单、正文和页脚有自定义颜色,并且在每个页面加载时都有一个有点烦人的Hello World弹出窗口,如下图所示:
odoo的CMS依赖于名为QWeb的XML模板引擎,我们将在下一节中详细介绍。资源包通过QWeb模板引入。在步骤1、2、3中,我们扩展了web.assets_frontend文件加载样式及js文件。我们选择web.assets_frontend是因为每一个网页都会加载这些文件。
步骤4,我们添加了CSS文件,用于设置网站的背景颜色。
小贴士
对于CSS/SCSS文件,有时顺序很重要。因此,如果您需要覆盖在另一个附加组件中定义的样式,则必须确保您的文件在您想要修改的原始文件之后加载。这可以通过调整视图的优先级字段或直接继承附加组件的视图来实现,该视图将引用注入到CSS文件中。详细信息,请参阅第9章“后端视图”中的“更改现有视图-视图继承配方”。
步骤5,我们添加了SCSS文件。odoo支持SCSS的预处理程序,将自动将SCSS编译为CSS文件。在我们的例子中,我们设置了几个变量及使用了darken的函数(可将$my-text-color变暗15%)。SCSS预处理器还有很多其他功能;如果你想了解更多关于SCSS的信息,请参考http://sass-lang.com/。
步骤6,我们添加了js文件,用于在页面加载完后弹框。为了避免JavaScript的排序问题,Odoo使用了一种非常类似于RequireJS的机制。在我们的JavaScript文件中,我们调用了odoo.define(),它需要两个参数:您想要定义的名称空间和包含实际实现的函数。如果您正在开发一个广泛使用JavaScript的复杂产品,那么您可以将代码划分为逻辑上不同的部分,并在不同的函数中定义它们。这将非常有用,因为您可以通过require导入函数来重用它们。此外,要定义模块的命名空间,请添加附加组件的名称,将其作为前缀,并用点分隔,以避免将来的命名冲突。如web模块下的,web.core和web.data。
对于第二个参数,definition函数只接收一个参数require,这个函数可以用来获取对其他模块中定义的JavaScript名称空间的引用。在所有与Odoo的交互中使用这个,并且永远不要依赖全局Odoo对象。
然后,您自己的函数可以返回一个对象,该对象指向您希望为其他附加组件提供的引用,或者如果没有此类引用,则不指向任何引用。如果你已经从你的函数返回了一些引用,你可以在另一个函数中使用它们,如下面的例子所示:
odoo.define('my_module', function (require) {
var test = {
key1: 'value1',
key2: 'value2'
};
var square = function (number) {
return 2 * 2;
};
return {
test: test,
square: square
}
});
// In another file
odoo.define('another_module', function (require) {
var my_module = require('my_module');
console.log(my_module.test.key1);
console.log('square of 5 is', my_module.square(5));
});
为了提高性能,Odoo只在前端加载最少的JavaScript。一旦页面被完全加载,资源中的所有其他JavaScript将被惰性加载,并且最小的可用资源拥有web.assets_frontend_minimal_js ID。
我们将在第四章“应用模型”中开发的my_library附加组件中添加网站功能。我们感兴趣的是允许用户浏览图书馆,如果他们以适当的权限登录,允许他们从网站界面编辑图书详细信息。
本节,我们将使用来自https://github.com/ PacktPublishing/oodoo-14-developing-cookbook-fourth-edition /tree/master/Chapter14/00_initial_module/my_library目录的my_library,该目录来自本书的GitHub存储库。
在controllers/main.py中添加展示图书列表的控制器,如下所示:
from odoo import http
from odoo.http import request
class Main(http.Controller):
@http.route('/books', type='http', auth="user",
website=True)
def library_books(self):
return request.render('my_library.books', {
'books': request.env['library.book'].search([]),
})
在views/templates.xml中添加最小模板(确保您已经在清单中添加了views/templates.xml文件):
在website.layout,添加可拖拽元素(class="oe_structure"),如下:
在website.layout中添加代码块,以展示图书的信息,如下:
在website.layout中添加不可编辑的元素:
在浏览器中打开http://your-server-url:8069/books,您将能够看到图书列表和作者。通过这段代码,用户可以看到图书及其详细信息的列表。如果有适当的权限,用户还可以更改图书细节和其他文本。
步骤1,我们有一个路由处理器接收用户自定义参数。这些参数将从处理器传递给QWeb模板。
步骤2、3、4、5,我们创造了一个名为Books的模板,用于生成用于展示图书的HTML的代码。代码由t元素包裹,并通过t-call属性调用website.layout模板,odoo将渲染website.layout模板,并将我们生成的HTML代码插入其中。website.layout包含必要的文件,比如Bootstrap、JQery、Font Awesome等。这些文件用于设计web页面。website.layout还包含了默认的头部、尾部、代码块及页面编辑功能。这样,我们得到一个完整的Odoo网页与菜单,页脚,页面编辑功能,而不必重复代码在所有页面。
步骤3、4、5,我们再website.layout中添加了HTML代码及QWeb模板的属性。HTML将展示图书的列表。一些常用的QWeb属性及他们用法如下:
要处理记录集或可迭代数据类型,你需要一个机构循环遍历列表,t-foreach,单个元素通过t元素实现。如下:
<t t-foreach="[1, 2, 3, 4, 5]" t-as="num">
<p><t t-esc="num"/></p>
</t>
渲染后结果如下:
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
你可以在任意元素中使用t-foreach及t-as属性。这时,在迭代器中,这个元素及其内容将会重复渲染。如下将生成上面一样的HTML代码:
<p t-foreach="[1, 2, 3, 4, 5]" t-as="num">
<t t-esc="num"/>
</p>
在t-foreach循环中,还有几个额外的变量,变量名将根据t-as设置的value组合而来。如前面的t-as=book的例子,book-odd变量将在迭代次数为奇数时为True,偶数时为False。在本例中,我们使用这个方法来为我们的卡片设置交替的背景颜色。
以下是其他可用的变量:
重要小贴士
这里的示例基于我们的场景。在本例中,您需要用t-as属性的给定值替换book。
QWeb模板可以动态设置属性值。这可以通过以下三种方式实现。
第一种方法是通过t-att-$attr_name。在模板呈现时,创建了一个属性$attr_name;它的值可以是任何有效的Python表达式。这是通过当前上下文计算的,结果设置为属性的值,如下所示:
<div t-att-total="10 + 5 + 5"/>
渲染后为:
<div total="20"></div>
第二种方法是通过t-attf-$attr_name。这与前面的选项类似。唯一的区别是只有字符串之间的{{..}}和#{…}会被计算。主要用于计算类,如下例所示:
<t t-foreach="['info', 'danger', 'warning']" t-as="color">
<div t-attf-class="alert alert-#{color}">
Simple bootstrap alert
</div>
</t>
渲染后为:
<div class="alert alert-info">
Simple bootstrap alert
</div>
<div class="alert alert-danger">
Simple bootstrap alert
</div>
<div class="alert alert-warning">
Simple bootstrap alert
</div>
第三种方法是通过t-att=mapping属性。该选项在将呈现字典数据的模板转换为属性和值之后接受字典。看看下面的例子:
<div t-att="{'id': 'my_el_id', 'class': 'alert alert- danger'}"/>
渲染后如下:
<div id="my_el_id" class="alert alert-danger"/>
h3和div标签使用t-field属性。t-field的值必须是长度为1的数据集。这可以在页面以编辑模式打开的时候可编辑。当用户保存后修改的值可更新到数据库。当然,当前用户必须具备访问权限才可以哦。通过t-options属性,你可以将一个字典传递给字段渲染器,包括想要使用的widget。目前,后端还没有大量的小部件集合,所以这里的选择有点有限。例如,你想展示一个图片,可如下:
<span t-field="author.image_small" t-options="{'widget': 'image'}"/>
t-field有一些限制。它仅作用于数据集且不能用于元素。你需要使用诸如或
的HTML元素。t-esc与t-field类似,但它并不局限于数据集,并且可用于各种类型的字段,但是它不可编辑。 另一个不同点是,t-field是会根据用户语言调整展示值的。而t-esc展示的数据库中的原始值。例如,对于英语用户,通过t-field展示datetime字段的值时,将展示12/15/2018 17:12:13格式。而使用t-esc的时候,将展示2018-12-15 17:12:13(若当地时区与UTC时区不同,则时间也会不同哦)
注意,显示出版日期的部分由t元素包装,t-if属性设置。此属性计算规则符合python的代码逻辑,元素只有在判断条件为true的时候才进行渲染。如下的例子,只有设置了出版日期的时候显示div。然而,在复杂的逻辑下,还需要用到t-elif和t-else,如下:
<div t-if="state == 'new'">
Text will be added of state is new.
</div>
<div t-elif="state == 'progress'">
Text will be added of state is progress.
</div>
<div t-else="">
Text will be added for all other stages.
</div>
QWeb模板还能够在模板本身中定义变量。定义模板之后,可以在后续模板中使用该变量。你可以这样设置变量:
<t t-set="my_var" t-value="5 + 1"/>
<t t-esc="my_var"/>
如果您正在开发一个大型应用程序,管理大型模板可能会很困难。QWeb模板支持子模板,因此您可以将大型模板划分为较小的子模板,并且可以在多个模板中重用它们。对于子模板,你可以使用t-call属性,就像下面这个例子:
<template id="first_template">
<div> Test Template </div>
</template>
<template id="second_template">
<t t-call="first_template"/>
</template>
用户可以在编辑模式下直接修改记录内容。通过t-field加载的数据默认是可编辑的。
如果你想配置可编辑、可拖拽的元素,那么可将元素的class配置为oe_structure。在我们的例子中,我们在顶层模板添加了该类。
如果你想禁用网站某个区域的编辑功能,可设置contenteditable=False属性。步骤5中,我们在
设置了该属性。
小贴士
To make the page multi-website-compatible, when you edit a page/view through the website editor, Odoo will create a separate copy of the page for that website. This means that subsequent code updates will never make it to the edited website page. In order to also get the ease of use of inline editing and the possibility of updating your HTML code in subsequent releases, create one view that contains the semantic HTML elements and a second one that injects editable elements. Then, only the latter view will be copied, and you can still have updates for the parent view.
对于这里使用的其他CSS类,请参考Bootstrap的文档。
在步骤1中,我们已经声明了渲染模板的路由。如果您注意到,我们在route()中使用了website=True参数,它将在模板中传递一些额外的上下文,如菜单、用户语言、公司等等。这将在网站上使用。布局,以呈现菜单和页脚。参数website=True还允许在网站中支持多语言。它还以更好的方式显示异常。
在函数末尾,我们返回了渲染的模板。
我们可以通过inherit_id继承已有的模板,并通过xpath定位修改的位置实现对现有模板的调整。例如,我们想在Authors标签旁展示作者的数量,可以通过如下方式实现:
<template id="books_ids_inh" inherit_id="my_library.books">
<xpath expr="//div[@id='card_body']/b" position="replace">
<b class="mt8"> Authors (<t t-esc="len(book.author_ ids)"/>) </b>
</xpath>
</template>
QWeb模板其实是qweb类型的普通视图。template标签是带有特定属性record元素的缩写。后台其实创建了一个ir.ui.view模型qweb类型的新纪录。通过tempalte标签的name及inherit_id属性,可以设置记录的inherit_id字段。
在下一节中,我们将学习如何管理动态路由。
关于QWeb模板的参考如下:
在网站开发项目中,我们经常需要创建动态的路由。比如,在电商中,每一个商品都有详细的页面且URL不同。在本节中,我们将展示每本书的详细内容。
我们会使用之前的my_library模块。为了使每本书页面更吸引人,我们将添加一些字段。如下:
class LibraryBook(models.Model):
_name = 'library.book'
name = fields.Char('Title', required=True)
date_release = fields.Date('Release Date')
author_ids = fields.Many2many('res.partner', string='Authors')
image = fields.Binary(attachment=True)
html_description = fields.Html()
在main.py添加新路由
@http.route('/books/
def library_book_detail(self, book):
return request.render( 'my_library.book_detail', {'book': book, })
添加模板:
添加按钮,导航到图书的详细页面:
步骤1,我们创建了动态路由。其中
步骤2,我们新建了一个展示图书详细页面的QWeb模板。其中html_description字段是html类型的值。odoo将自动添加可拖拽的代码到html类型的值。
步骤3,添加了到每本书的链接。
小贴士
模型路由还支持域过滤。例如,如果要基于某个条件限制某些书籍,可以按如下方式将域传递到路由:
/books/
/submit 这将限制名为"Book 1"的图书。
Odoo使用werkzeug来处理HTTP请求。Odoo在werkzeug周围添加了一个薄薄的包装,以方便处理路由。上面的例子中
/page/int:page 接受整数值。
/page/
/pages/ 接受字符串。
/pages/
更多详细内容可参考 http://
werkzeug.pocoo.org/docs/0.14/routing/.
odoo网站编辑器提供了几种编辑功能区的方式,可拖拽可编辑。本节将介绍如何构建自己的功能区。这些功能区称为代码段。有几种类型的代码段,通常可分为静态和动态。静态代码段是固定的,除非用户主动修改。动态区域是依赖于数据库数据变化的。本节我们将介绍如何创建静态代码段。
代码段其实是将被注入到添加模块区域的QWeb视图。我们将创建一个展示图书的image和图书的title。你可以在页面上拖放功能块,可以编辑图片及标题。
添加新文件views/snippets.xml
添加QWeb视图如下:
将代码段添加到website.snippets
添加封面及缩略图/my_library/ static/src/img。
静态代码段其实就是HTML代码的区块。步骤1,我们创建了QWeb的模板。在HTML中,我们使用了Bootstrap的架构。静态代码段可通过拖拽的形式加载到页面上。一般而言,在代码段中使用section元素及Bootstrap类将会非常方便,因为odoo为我们提供了开箱即用的页面、背景、尺寸的编辑功能。
步骤2,我们在代码列表中注册我们的代码段。可通过继承website.snippets实现。在网站的编辑器中,将被分为不同的区域。在我们的例子中,我们可通过xpath注册代码段。为了展示我们的代码段,可通过标签加t-snippet属性实现。t-snippet属性值是XML ID值。我们还可以使用t-thumbnail用于展示代码区的缩略图。
小贴士
website.snippets模板包含了所有的默认代码段,你可以在/addons/website/views/snippets/snippets.xml详细了解。
当你使用合适的Bootstrap架构时,odoo将自动添加一些默认的选项。比如,在我们的例子中,你可以设置背景色,背景图,宽度,高度等。在/addons/website/views/snippets/snippets.xml中可以看到全部的选项。下一节,我们将了解如何添加我们自己的可选项。
步骤3,我们已经在结构块下面列出了我们的代码片段。更新模块后,就可以拖放代码段了。在步骤4中,我们刚刚为代码段缩略图添加了一个图像。
在这种情况下,不需要额外的JavaScript。Odoo的编辑器提供了很多开箱即用的选项和控件,它们对于静态代码段来说已经足够了。您将在 website/views/snippets.xml中找到所有现有的代码段和选项。
Snippet选项还支持data exclude、data drop near和data-drop-in属性,这些属性决定了将代码段从代码段栏中拖出时可以将其放置在何处。这些也是jQuery选择器,在这个方法的第3步中,我们没有使用它们,因为我们允许将代码片段放在内容可以到达的任何地方。
本节,我们将学习如何创建动态代码片段。
在views/snippets.xml添加QWeb模板
Latest books
Name
Release date
注册代码片段并添加选项改变代码行为:
在图书片添加选项:
添加/static/src/snippets.js文件
odoo.define('book.dynamic.snippet', function (require) {
'use strict';
var publicWidget = require('web.public.widget');
// Add step 5 here
});
添加public小部件渲染book代码片段:
publicWidget.registry.books = publicWidget.Widget.extend({
selector: '.book_snippet',
disabledInEditableMode: false,
start: function () {
var self = this;
var rows = this.$el[0].dataset.numberOfBooks || '5';
this.$el.find('td').parents('tr').remove();
this.rpc({
model: 'library.book',
method: 'search_read',
domain: [],
fields: ['name', 'date_release'],
orderBy: [{
name: 'date_release',
asc: false
}],
limit: parseInt(rows)
}).then(function (data) {
.each(data, function (book) {
self.$el.append(
$('
添加js文件
更新模块,我们新增了名为Latest books的代码段,提供了一个可选择展示最新添加几本书的选项。
步骤1,我们添加了QWeb模板,包含了table的基础架构,并动态生成图书的行。
步骤2,我们注册了动态代码段,我们添加了改变代码行为的自定义的选项。我们添加的第一个选项是选择Table样式。第二个选项是图书的数量。我们使用
如果仔细观察这些选项,就会发现选项按钮有data-select-class和data-select-data-attribute属性。这将让Odoo知道当用户选择一个选项时要更改哪个属性。data-select- class将在用户选择该选项时设置元素的class属性,而data-select-data-attribute将设置元素的自定义属性和值。注意,它将使用data-attribute-name的值来设置属性。
现在,我们已经添加了代码片段和代码片段选项。如果此时拖放代码片段,则只会看到表头和代码片段选项。更改snippet选项将更改表样式,但还没有图书数据。为此,我们需要编写一些JavaScript代码来获取数据并将其显示在表中。在步骤3中,我们已经添加了JavaScript代码,用于在表中呈现图书数据。要将JavaScript对象映射到HTML元素,Odoo使用PublicWidget。现在,可以通过require('web.public.widget')模块获得PublicWidget。使用PublicWidget的关键属性是选择器属性。在selector属性中,您需要使用元素的CSS选择器,Odoo将自动将元素与PublicWidget绑定。您可以访问$el属性中的相关元素。除了_rpc之外,其余的代码都是基本的JavaScript和jQuery。_rpc方法用于发出网络请求并获取图书数据。我们将在第15章“Web客户端开发”的服务器配方的RPC调用中学习更多关于_rpc方法的知识。
如果您想创建自己的代码片段选项,可以在代码片段选项上使用t-js选项。之后,您需要在JavaScript代码中定义自己的选项。详细内容可参见 addons/website/static/src/js/editor/snippets.options.js
在网站开发模式下,我们经常需要获取用户输入。本节,我们将为用户创建一个针对图书反馈问题的html 表格。
本节,我们使用my_library模块,我们需要一个新的模型存储问题信息。
1. 在library.book模型中添加字段及book.issues模型,如下:
class LibraryBook(models.Model):
_name = 'library.book'
name = fields.Char('Title', required=True)
date_release = fields.Date('Release Date')
author_ids = fields.Many2many('res.partner', string='Authors')
image = fields.Binary(attachment=True)
html_description = fields.Html()
book_issue_id = fields.One2many('book.issue', 'book_id')
class LibraryBookIssues(models.Model):
_name = 'book.issue'
book_id = fields.Many2one('library.book', required=True)
submitted_by = fields.Many2one('res.users')
isuue_description = fields.Text()
2. 在图书form视图中添加book_issues_id字段:
<group string="Book Issues">
<field name="book_issue_id" nolabel="1">
<tree>
<field name="create_date"/> <field name="submitted_by"/>
<field name="isuue_description"/>
</tree>
</field>
</group>
3. 添加book.issue的访问记录
acl_book_issues,library.book_issue,model_book_issue,group_librarian,1,1,1,1
1. 在main.py添加路由
@http.route("/books/submit_issues", type="http", auth="user", website=True)
def books_issues(self, **post):
if post.get("book_id"):
book_id = int(post.get("book_id"))
issue_description = post.get("issue_description")
request.env["book.issue"].sudo().create(
{
"book_id": book_id,
"issue_description": issue_description,
"submitted_by": request.env.user.id,
}
)
return request.redirect("/books/submit_ issues?submitted=1")
return request.render(
"my_library.books_issue_form",
{
"books": request.env["library.book"].search([]),
"submitted": post.get("submitted", False),
},
)
2. 添HTML form:
<template id="books_issue_form" name="Book Issues Form">
<t t-call="website.layout">
<div class="container mt32">
<!-- add the page elements here(step 3 and 4)-->
</div>
</t>
</template>
3. 为页面添加条件头,如下所示:
<t t-if="submitted">
<h3 class="alert alert-success mt16 mb16">
<i class="fa fa-thumbs-up"/>
Book submitted successfully
</h3>
<h1> Report the another book issue </h1>
</t>
<t t-else="">
<h1> Report the book issue </h1>
</t>
4. 添加
手机扫一扫
移动阅读更方便
你可能感兴趣的文章