创建基本模块
在Odoo中,任务通过创建模块来执行。
模块通过添加新行为或通过更改现有行为(包括其他模块添加的行为)来定制Odoo安装的行为。
Odoo的脚手架可以设置一个基本的模块。要快速入门,只需调用:
$ ./odoo-bin scaffold Academy my-modules
这将自动创建一个my-modules 模块的目录与 academy内部模块。如果需要,该目录可以是现有的模块目录,但该目录中的模块名称必须是唯一的。
演示模块
我们有一个“完整”模块准备安装。
虽然它绝对没有我们可以安装它:
·
启动Odoo服务器
·
$ ./odoo-bin --addons-path addons,my-modules
·
· 创建一个包含演示数据的新数据库
· 去设置‣模块‣模块
· 在右上角删除已安装的过滤器并搜索 学院
· 单击学院模块的安装按钮
到浏览器
控制器解释浏览器请求并发回数据。
添加一个简单的控制器,确保它被导入__init__.py(所以Odoo可以找到它):
学院/ controllers.py
# -*- coding: utf-8 -*-from odoo import http
class Academy(http.Controller):
@http.route('/academy/academy/', auth='public')
def index(self, **kw):
return "Hello, world"
# @http.route('/academy/academy/objects/', auth='public')
# def list(self, **kw):
关闭你的服务器(^C)然后重新启动它:
$ ./odoo-bin --addons-path addons,my-modules
并打开一个页面到http:// localhost:8069 / academy / academy /,你应该看到你的“页面”出现:
模板
在Python中生成HTML不是很愉快。
通常的解决方案是具有占位符和显示逻辑的模板,伪文档。Odoo允许任何Python模板系统,但提供自己的与其他功能集成的QWeb模板系统。
创建模板并确保模板文件已在__manifest__.py清单中注册 ,并更改控制器以使用我们的模板:
academy/ controllers.py
class Academy(http.Controller):
@http.route('/academy/academy/', auth='public')
def index(self, **kw):
return http.request.render('academy.index', {
'teachers': ["Diana Padilla", "Jody Caroll", "Lester Vaughn"],
})
# @http.route('/academy/academy/objects/', auth='public')
# def list(self, **kw):
academy/templates.xml
<odoo>
<data>
<template id="index">
<title>Academy</title>
<t t-foreach="teachers" t-as="teacher">
<p><t t-esc="teacher"/></p>
</t>
</template>
<!-- <template id="object"> -->
<!-- <h1><t t-esc="object.display_name"/></h1> -->
<!-- <dl> -->
模板t-foreach对所有教师进行迭代()(通过 模板上下文),并在其自己的段落中打印每个教师。
最后重新启动Odoo并通过转到设置‣模块‣模块‣学院并单击升级来更新模块的数据(以安装模板)。
Tip
Alternatively, Odoo can be restarted and update modules at the same time:
$ odoo-bin --addons-path addons,my-modules -d academy -u academy
Going to http://localhost:8069/academy/academy/ should now result in:
在Odoo中存储数据
Odoo模型映射到数据库表。
在上一节中,我们刚刚显示了一个在Python代码中静态输入的字符串列表。这不允许修改或持久存储,所以我们现在将数据移动到数据库。
定义数据模型
定义一个教师模型,并确保它被导入,__init__.py从而正确加载:
academy/models.py
from odoo import models, fields, api
class Teachers(models.Model):
_name = 'academy.teachers'
name = fields.Char()
Then setup basic access control for the model and add them to the manifest:
academy/__manifest__.py
# always loaded 'data': [
'security/ir.model.access.csv',
'templates.xml',
],
# only loaded in demonstration mode
academy/security/ir.model.access.csv
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_academy_teachers,access_academy_teachers,model_academy_teachers,,1,0,0,0
这只是给perm_read所有用户的read access()(group_id:id 留空)。
注意
数据文件(XML或CSV)必须添加到模块清单中,Python文件(模型或控制器)不但必须从__init__.py(直接或间接)导入,
警告
管理员用户绕过访问控制,即使没有给予访问,他们也可以访问所有模型
演示数据
第二步是向系统添加一些演示数据,以便轻松测试。这是通过添加必须从清单链接的demo 数据文件完成的:
academy/demo.xml
<odoo>
<data>
<record id="padilla" model="academy.teachers">
<field name="name">Diana Padilla</field>
</record>
<record id="carroll" model="academy.teachers">
<field name="name">Jody Carroll</field>
</record> <record id="vaughn" model="academy.teachers">
<field name="name">Lester Vaughn</field>
</record>
</data></odoo>
小费
数据文件可用于演示和非演示数据。演示数据仅以“演示模式”加载,可用于流测试和演示,非演示数据始终加载并用作初始系统设置。
在这种情况下,我们使用演示数据,因为系统的实际用户想要输入或导入自己的教师列表,此列表仅对测试有用。
访问数据
最后一步是改变模型和模板来使用我们的演示数据:
1. 从数据库中获取记录,而不是静态列表
2. 因为search()返回一组与过滤器匹配的记录(“所有记录”),请更改模板以打印每位教师name
academy/controllers.py
class Academy(http.Controller):
@http.route('/academy/academy/', auth='public')
def index(self, **kw):
Teachers = http.request.env['academy.teachers']
return http.request.render('academy.index', {
'teachers': Teachers.search([])
})
# @http.route('/academy/academy/objects/', auth='public')
academy/templates.xml
<template id="index">
<title>Academy</title>
<t t-foreach="teachers" t-as="teacher">
<p><t t-esc="teacher.id"/> <t t-esc="teacher.name"/></p>
</t>
</template>
<!-- <template id="object"> -->
重新启动服务器并更新模块(为了更新清单和模板并加载演示文件),然后导航到 http:// localhost:8069 / academy / academy /。该页面应该看起来略有不同:名称应该只是一个数字(教师的数据库标识符)的前缀。
网站支持
Odoo捆绑了一个致力于构建网站的模块。
到目前为止,我们直接使用了控制器,但Odoo 8通过该website模块增加了更深入的集成和其他一些服务(如默认样式,主题) 。
1. 首先,添加website为依赖academy
2. 然后website=True在控制器上添加标志,这将在请求对象上设置一些新变量,并允许在我们的模板中使用网站布局
3. 使用模板中的网站布局
academy/__manifest__.py
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['website'],
# always loaded
'data': [
academy/controllers.py
from odoo import http
class Academy(http.Controller):
@http.route('/academy/academy/', auth='public', website=True)
def index(self, **kw):
Teachers = http.request.env['academy.teachers']
return http.request.render('academy.index', {
academy/templates.xml
<odoo>
<data>
<template id="index">
<t t-call="website.layout">
<t t-set="title">Academy</t>
<div class="oe_structure">
<div class="container">
<t t-foreach="teachers" t-as="teacher">
<p><t t-esc="teacher.id"/> <t t-esc="teacher.name"/></p> </t>
</div>
</div>
</t>
</template>
<!-- <template id="object"> -->
在更新模块后重新启动服务器(为了更新清单和模板),访问http:// localhost:8069 / academy / academy /应该会产生一个更美观的页面,其中包含品牌和许多内置页面元素(top级别菜单,页脚,...)
网站布局还为版本工具提供支持:单击 登录(右上角),填写(admin/ admin默认)中的凭据,然后单击登录。
你现在在Odoo“正确”:管理界面。现在点击网站菜单项(左上角)。
我们回到网站,但作为管理员,可以访问网站支持提供的高级版本功能:
· 一个模板代码编辑器(自定义‣HTML编辑器),您可以在其中查看和编辑用于当前页面的所有模板
· 该编辑在左上角的开关按钮为“编辑模式”,其中块(片段)和富文本编辑可用
· 许多其他功能,如移动预览或SEO
网址和路由
控制器方法通过 装饰器与路由相关联,route()装饰器采用路由字符串和多个属性来定制其行为或安全性。
我们已经看到一个“文字”路由字符串,它完全匹配了一个URL部分,但是路由字符串也可以使用匹配URL位的转换器模式,并将它们作为局部变量。例如,我们可以创建一个新的控制器方法,该方法需要一些URL并将其打印出来:
academy/controllers.py
'teachers': Teachers.search([])
})
@http.route('/academy/<name>/', auth='public', website=True)
def teacher(self, name):
return '<h1>{}</h1>'.format(name)
# @http.route('/academy/academy/objects/', auth='public')
# def list(self, **kw):
# return http.request.render('academy.listing', {
重新启动Odoo,访问 http://localhost:8069/academy/Alice/ and http://localhost:8069/academy/Bob/并查看区别。
如名称所示,转换器模式不仅仅是提取,它们也进行验证和转换,因此我们可以将新控制器更改为仅接受整数:
academy/controllers.py
'teachers': Teachers.search([])
})
@http.route('/academy/<int:id>/', auth='public', website=True)
def teacher(self, id):
return '<h1>{} ({})</h1>'.format(id, type(id).__name__)
# @http.route('/academy/academy/objects/', auth='public')
重新启动Odoo,访问 http://localhost:8069/academy/2,注意旧值是一个字符串,但是新的值被转换为整数。尝试访问 http://localhost:8069/academy/Carol/,并注意到没有找到该页面:由于“Carol”不是整数,所以路由被忽略,并且没有找到路由。
Odoo提供了一个额外的转换器model,用于在给定其ID时直接提供记录。让我们用这个方法为教师传记创建一个通用页面:
academy/controllers.py
'teachers': Teachers.search([])
})
@http.route('/academy/<model("academy.teachers"):teacher>/', auth='public', website=True)
def teacher(self, teacher):
return http.request.render('academy.biography', {
'person': teacher
})
# @http.route('/academy/academy/objects/', auth='public')
academy/templates.xml
</div>
</t>
</template>
<template id="biography">
<t t-call="website.layout">
<t t-set="title">Academy</t>
<div class="oe_structure"/>
<div class="oe_structure">
<div class="container">
<p><t t-esc="person.id"/> <t t-esc="person.name"/></p>
</div>
</div>
<div class="oe_structure"/>
</t> </template>
<!-- <template id="object"> -->
<!-- <h1><t t-esc="object.display_name"/></h1> -->
<!-- <dl> -->
then change the list of model to link to our new controller:
academy/templates.xml
<div class="oe_structure">
<div class="container">
<t t-foreach="teachers" t-as="teacher">
<p><a t-attf-href="/academy/{{ slug(teacher) }}"> <t t-esc="teacher.name"/></a>
</p>
</t>
</div>
</div>
<div class="oe_structure"/>
<div class="oe_structure">
<div class="container">
<h3><t t-esc="person.name"/></h3>
</div>
</div>
<div class="oe_structure"/>
重新启动Odoo并升级模块,然后您可以访问每个教师的页面。作为一个练习,尝试添加块到老师的页面来撰写传记,然后去另一个老师的页面等等。您会发现,您的传记是在所有老师之间共享的,因为将模块添加到 模板中,并且传记模板在所有教师之间共享,当一个页面被编辑时,它们都被同时编辑。
现场版
记录中特定的数据应保存在该记录中,因此我们可以向我们的老师添加一个新的传记字段:
academy/models.py
_name = 'academy.teachers'
name = fields.Char()
biography = fields.Html()
academy/templates.xml
<div class="oe_structure">
<div class="container">
<h3><t t-esc="person.name"/></h3>
<div><t t-esc="person.biography"/></div>
</div>
</div>
<div class="oe_structure"/>
重新启动Odoo并更新视图,重新载入教师的页面,并且该字段是不可见的,因为它不包含任何内容。
对于记录字段,模板可以使用特殊t-field指令,允许使用字段特定的界面从网站编辑字段内容。更改要使用的人员模板t-field:
academy/templates.xml
<div class="oe_structure"/>
<div class="oe_structure">
<div class="container">
<h3 t-field="person.name"/>
<div t-field="person.biography"/>
</div>
</div>
<div class="oe_structure"/>
重新启动Odoo并升级模块,现在有一个占位符的老师的名字和一个新的区块的编辑模式。内容丢失存储在相应的教师biography领域,因此具体到该老师。
老师的名字也是可编辑的,保存后,更改在索引页面上可见。
t-field也可以采用取决于准确字段的格式化选项。例如,如果我们显示教师记录的修改日期:
academy/templates.xml
<div class="oe_structure">
<div class="container">
<h3 t-field="person.name"/>
<p>Last modified: <i t-field="person.write_date"/></p> <div t-field="person.biography"/>
</div>
</div>
它以非常“计算”的方式显示,难以阅读,但我们可以要求一个可读的版本:
academy/templates.xml
<div class="oe_structure">
<div class="container">
<h3 t-field="person.name"/>
<p>Last modified: <i t-field="person.write_date" t-options='{"format": "long"}'/></p> <div t-field="person.biography"/>
</div>
</div>
或相对显示:
academy/templates.xml
<div class="oe_structure">
<div class="container">
<h3 t-field="person.name"/>
<p>Last modified: <i t-field="person.write_date" t-options='{"widget": "relative"}'/></p>
<div t-field="person.biography"/>
</div>
</div>
管理和ERP整合
对Odoo行政部门的简要和不完整的介绍
网站支持部分简要介绍了Odoo行政部门。我们可以使用管理员‣管理员在菜单中返回(或者如果您已登出,请登录)。
Odoo后端的概念结构很简单:
1. 首先是菜单,树(菜单可以有子菜单)的记录。没有孩子的菜单映射到...
2. 动作。操作有各种类型:链接,报表,Odoo应执行或数据显示的代码。数据显示操作称为窗口动作,并告诉Odoo 根据一组视图显示给定的模型 ...
3. 视图具有类型,与其对应的广泛类别(列表,图形,日历)和定制模型在视图内显示的方式的架构。
在Odoo管理中编辑
默认情况下,Odoo模型对于用户基本上是不可见的。为了使其可见,必须通过一个通常通过菜单可以实现的动作来实现。
我们为我们的模型创建一个菜单:
academy/__manifest__.py
'data': [
'security/ir.model.access.csv',
'templates.xml', 'views.xml', ],
# only loaded in demonstration mode
'demo': [
academy/views.xml
<odoo>
<data>
<record id="action_academy_teachers" model="ir.actions.act_window">
<field name="name">Academy teachers</field>
<field name="res_model">academy.teachers</field>
</record>
<menuitem sequence="0" id="menu_academy" name="Academy"/>
<menuitem id="menu_academy_content"
parent="menu_academy"
name="Academy Content"/>
<menuitem id="menu_academy_content_teachers"
parent="menu_academy_content"
action="action_academy_teachers"/>
然后访问左上角的http:// localhost:8069 / web /应该是默认选择的菜单 学院,因为它是第一个菜单,并且打开了一个教师列表。从列表中可以 创建新的教师记录,并切换到“表单”副作用视图。
如果没有定义如何呈现记录(一个视图),Odoo将自动创建一个基本的记录 。在我们的例子中,它现在可用于“列表”视图(仅显示教师的名称),但在“窗体”视图中,HTML biography字段与name字段并排显示,并且没有给出足够的空间。让我们定义一个自定义的表单视图,让观看和编辑教师记录更好的体验:
academy/views.xml
<field name="name">Academy teachers</field>
<field name="res_model">academy.teachers</field>
</record>
<record id="academy_teacher_form" model="ir.ui.view">
<field name="name">Academy teachers: form</field>
<field name="model">academy.teachers</field>
<field name="arch" type="xml">
<form>
<sheet>
<label for="name"/> <field name="name"/>
<label for="biography"/>
<field name="biography"/>
</sheet>
</form>
</field>
</record>
<menuitem sequence="0" id="menu_academy" name="Academy"/>
<menuitem id="menu_academy_content" parent="menu_academy"
模型之间的关系
我们已经看到一对直接存储在记录中的“基本”字段。有 一些基本领域。第二大类字段是关系型的,用于将记录彼此链接(模型中或模型之间)。
为了演示,我们创建一个课程模型。每个课程应该有一个 teacher领域,连接到一个单一的教师记录,但每个教师可以教授许多课程:
academy/models.py
name = fields.Char()
biography = fields.Html()class Courses(models.Model):
_name = 'academy.courses'
name = fields.Char()
teacher_id = fields.Many2one('academy.teachers', string="Teacher")
academy/security/ir.model.access.csv
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_academy_teachers,access_academy_teachers,model_academy_teachers,,1,0,0,0
access_academy_courses,access_academy_courses,model_academy_courses,,1,0,0,0
我们还可以添加视图,以便我们可以看到和编辑课程的老师:
academy/views.xml
</form>
</field>
</record>
<record id="action_academy_courses" model="ir.actions.act_window">
<field name="name">Academy courses</field>
<field name="res_model">academy.courses</field>
</record> <record id="academy_course_search" model="ir.ui.view">
<field name="name">Academy courses: search</field>
<field name="model">academy.courses</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="teacher_id"/>
</search>
</field>
</record>
<record id="academy_course_list" model="ir.ui.view">
<field name="name">Academy courses: list</field>
<field name="model">academy.courses</field>
<field name="arch" type="xml">
<tree string="Courses">
<field name="name"/>
<field name="teacher_id"/>
</tree>
</field>
</record>
<record id="academy_course_form" model="ir.ui.view">
<field name="name">Academy courses: form</field>
<field name="model">academy.courses</field>
<field name="arch" type="xml">
<form>
<sheet>
<label for="name"/>
<field name="name"/>
<label for="teacher_id"/>
<field name="teacher_id"/>
</sheet>
</form>
</field>
</record>
<menuitem sequence="0" id="menu_academy" name="Academy"/>
<menuitem id="menu_academy_content" parent="menu_academy"
name="Academy Content"/>
<menuitem id="menu_academy_content_courses"
parent="menu_academy_content"
action="action_academy_courses"/>
<menuitem id="menu_academy_content_teachers"
parent="menu_academy_content"
还可以直接从教师页面创建新课程,或者查看他们所教授的所有课程,因此添加 the inverse relationship到教师 模型中:
academy/models.py
name = fields.Char()
biography = fields.Html()
course_ids = fields.One2many('academy.courses', 'teacher_id', string="Courses")
class Courses(models.Model):
_name = 'academy.courses'
academy/views.xml
<field name="arch" type="xml">
<form>
<sheet>
<label for="name"/> <field name="name"/>
<label for="biography"/>
<field name="biography"/>
<field name="course_ids">
<tree string="Courses" editable="bottom">
<field name="name"/>
</tree>
</field>
</sheet>
</form>
讨论和通知
Odoo提供的技术模型不直接满足业务需求,而是增加业务对象的功能,而无需手工构建。
其中之一是Chatter系统,Odoo的电子邮件和消息系统的一部分,可以向任何型号添加通知和讨论线程。该模型只需要将该字段添加到其窗体视图中,以显示讨论线程。讨论主题是每个记录。_inherit mail.threadmessage_ids
对于我们的学院,允许讨论课程来处理例如调度更改或教师和助理之间的讨论是有道理的:
academy/models.py
class Courses(models.Model):
_name = 'academy.courses'
_inherit = 'mail.thread'
name = fields.Char()
teacher_id = fields.Many2one('academy.teachers', string="Teacher")
academy/views.xml
<field name="name"/>
<label for="teacher_id"/>
<field name="teacher_id"/>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
在每个课程表格的底部,现在有一个讨论线索,并且系统的用户可以留下消息并跟随或取消关注与特定课程相关的讨论。
销售课程
Odoo还提供允许更直接地使用或选择业务需求的商业模式。例如,该website_sale模块基于Odoo系统中的产品设置一个电子商务网站。我们可以通过让我们的课程具体种类的产品轻松地使课程订阅可以销售。
而不是以前的古典继承,这意味着用产品模型取代我们的 课程模型,并将产品推广到原地(添加我们需要的任何东西)。
首先我们需要添加一个依赖关系,website_sale所以我们得到两个产品(通过sale)和电子商务接口:
academy/__manifest__.py
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['website_sale'],
# always loaded
'data': [
重新启动Odoo,更新您的模块,现在有一个Shop部分在网站上,列出了一些预填(通过演示数据)产品。
第二步是用课程替换课程模型product.template,并为课程添加新的产品类别:
academy/__manifest__.py
'security/ir.model.access.csv',
'templates.xml',
'views.xml', 'data.xml', ],
# only loaded in demonstration mode
'demo': [
academy/data.xml
<odoo><data>
<record model="product.public.category" id="category_courses">
<field name="name">Courses</field>
<field name="parent_id" ref="website_sale.categ_others"/>
</record></data></odoo>
academy/demo.xml
<record id="vaughn" model="academy.teachers">
<field name="name">Lester Vaughn</field>
</record>
<record id="course0" model="product.template">
<field name="name">Course 0</field>
<field name="teacher_id" ref="padilla"/>
<field name="public_categ_ids" eval="[(4, ref('academy.category_courses'), False)]"/> <field name="website_published">True</field>
<field name="list_price" type="float">0</field>
<field name="type">service</field>
</record>
<record id="course1" model="product.template">
<field name="name">Course 1</field>
<field name="teacher_id" ref="padilla"/>
<field name="public_categ_ids" eval="[(4, ref('academy.category_courses'), False)]"/> <field name="website_published">True</field>
<field name="list_price" type="float">0</field>
<field name="type">service</field>
</record>
<record id="course2" model="product.template">
<field name="name">Course 2</field>
<field name="teacher_id" ref="vaughn"/>
<field name="public_categ_ids" eval="[(4, ref('academy.category_courses'), False)]"/> <field name="website_published">True</field>
<field name="list_price" type="float">0</field>
<field name="type">service</field>
</record>
</data></odoo>
academy/models.py
name = fields.Char()
biography = fields.Html()
course_ids = fields.One2many('product.template', 'teacher_id', string="Courses")
class Courses(models.Model):
_inherit = 'product.template'
teacher_id = fields.Many2one('academy.teachers', string="Teacher")
academy/security/ir.model.access.csv
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_academy_teachers,access_academy_teachers,model_academy_teachers,,1,0,0,0
academy/views.xml
</form>
</field>
</record>
<menuitem sequence="0" id="menu_academy" name="Academy"/>
<menuitem id="menu_academy_content" parent="menu_academy"
name="Academy Content"/>
<menuitem id="menu_academy_content_teachers"
parent="menu_academy_content"
有了这个安装,现在有几个课程可以在商店,虽然他们可能需要寻找。
注意
· 在现场扩展模型,它inherited没有给它一个新的 _name
· product.template 已经使用了讨论系统,所以我们可以从扩展模型中删除它
· 我们创建了我们的课程,发表在默认情况下,使他们能够无需登录可见
改变现有观点
到目前为止,我们已经看到:
· 创建新车型
· 创造新观点
· 创建新记录
· 现有模式的改变
我们留下现有记录的更改和现有视图的更改。我们会在“ 商店”页面上进行。
查看更改是通过创建扩展视图来完成的,这些扩展视图应用在原始视图的顶部并进行更改。可以添加或删除这些更改视图,而无需修改原始内容,从而更容易地尝试出来并将其更改回来。
由于我们的课程是免费的,所以没有理由在商店页面上显示价格,所以我们将改变视图并隐藏价格,如果是0.第一个任务是找出哪个视图显示价格,这可以通过 自定义‣HTML编辑器完成,它可以让我们阅读渲染页面所涉及的各种模板。通过他们中的几个,“产品项目”看起来可能是罪魁祸首。
更改视图体系结构有三个步骤:
1. 创建一个新的视图
2. 通过将新视图设置inherit_id为修改视图的外部ID 来扩展视图以进行修改
3. 在架构中,使用xpath标签从修改的视图中选择和更改元素
academy/templates.xml
<div class="oe_structure"/>
</t>
</template>
<template id="product_item_hide_no_price" inherit_id="website_sale.products_item"> <xpath expr="//div[hasclass('product_price')]/b" position="attributes">
<attribute name="t-if">product.price > 0</attribute>
</xpath>
</template>
<!-- <template id="object"> -->
<!-- <h1><t t-esc="object.display_name"/></h1> -->
<!-- <dl> -->
我们将会改变的第二件事就是使产品类别侧栏默认可见:自定义‣产品类别可以打开和关闭产品类别树(用于过滤主显示)。
这是通过扩展模板customize_show和active字段进行的:扩展模板(如我们刚刚创建的模板)可以是 customize_show = True。此选项将在“ 自定义” 菜单中显示一个复选框,允许管理员激活或禁用它们(并轻松自定义其网页)。
我们只需要修改产品分类记录并将其默认设置为active =“True”:
academy/templates.xml
</xpath>
</template>
<record id="website_sale.products_categories" model="ir.ui.view">
<field name="active" eval="True"/>
</record>
<!-- <template id="object"> -->
<!-- <h1><t t-esc="object.display_name"/></h1> -->
<!-- <dl> -->
这样,当安装了学院模块时,产品类别侧栏将自动启用。