时间:2023-08-05 11:27:02 | 来源:网站运营
时间:2023-08-05 11:27:02 来源:网站运营
Odoo11.0官方开发文档-教程-网站的构建:创建一个基本模块$ ./odoo-bin scaffold Academy my-modules
这样会自动创建一个my-modules模块目录以及一个内部的academy 模块。该目录可以是一个您希望的已存在的模块目录。但是模块名称在目录中必须是唯一的。$ ./odoo-bin --addons-path addons,my-modules
# -*- coding: utf-8 -*-from odoo import httpclass 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/,您应该看到网页显示: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> <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-bin --addons-path addons,my-modules -d academy -u academy
现在转到http://localhost:8069/academy/academy/ 可以看到: from odoo import models, fields, apiclass Teachers(models.Model): _name = 'academy.teachers' name = fields.Char()
然后为模型设置基本的访问控制并把它们添加到清单文件: # always loaded 'data': [ 'security/ir.model.access.csv', 'templates.xml', ], # only loaded in demonstration mode
academy/security/ir.model.access.csvid,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlinkaccess_academy_teachers,access_academy_teachers,model_academy_teachers,,1,0,0,0
这样就简单的把读取的权限 (perm_read) 开给了所有用户(group_id:id 留空)。<odoo> <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></odoo>
数据文件可被用于演示和非演示数据。演示数据仅在“演示模式”中加载来用于流程测试和演示,非演示数据总是被加载并用于初始化系统设置。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/。页面看起来会稍有不同:名称会简单的以数字作为前缀(教员在数据库中的标识)。 'version': '0.1', # any module necessary for this one to work correctly 'depends': ['website'], # always loaded 'data': [
academy/controllers.pyfrom odoo import httpclass 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> <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/ 会得到一个更好看的页面,带有品牌和一些内建的页面元素(顶级菜单,页脚……等等) '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/和http://localhost:8069/academy/Bob/ 查看差异。 '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”不是一个整形,路由被忽略,找不到对应的值。 '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> -->
然后修改模型列表来关联到我们的新控制器: <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并升级模块,然后您可以访问到每个教员的页面。作为练习,添加一些区块到教员的页面来撰写一个传记,然后转到另外一个教员的页面做相同的操作。您会发现,您的传记在所有教员之间共享了,因为这些区块被添加到了模板中,而传记模板在所有教员中共享,因此其中一个页面被编辑后,它们都同时被修改了。 _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并更新视图,刷新教员页面……因为字段是空的所以并不可见。 <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中,和该教员对应。 <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>
它以为非常“电脑化”的方式显示并难以阅读,但我们可以请求一个人工可读的版本:<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>
或者一个相关的显示:<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>
'data': [ 'security/ir.model.access.csv', 'templates.xml', 'views.xml', ], # only loaded in demonstration mode 'demo': [
academy/views.xml<odoo> <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"/>
然后在左上角菜单中的Academy菜单访问http://localhost:8069/web/,它默认作为第一个菜单被选中,打开一个教员的列表。从列表中可以创建一条教员的记录,并通过记录视图切换到“表单”。 <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" name="Academy Content"/>
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.csvid,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlinkaccess_academy_teachers,access_academy_teachers,model_academy_teachers,,1,0,0,0access_academy_courses,access_academy_courses,model_academy_courses,,1,0,0,0
让我们也添加视图以便查看和编辑课程的教员: </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" action="action_academy_teachers"/>
从教员页面直接创建新课程或者查看所有他们教授的课程也是可以的,可以添加反向关系到教员的模型中: 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 <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> </field>
class Courses(models.Model): _name = 'academy.courses' _inherit = 'mail.thread' name = fields.Char() teacher_id = fields.Many2one('academy.teachers', string="Teacher")
academy/views.xml <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> </record>
每个课程表单的底部,有一个讨论主题和可能的系统用户,来对关联的特定课程进行留言、关注或者取消关注。 'version': '0.1', # any module necessary for this one to work correctly 'depends': ['website_sale'], # always loaded 'data': [
重启Odoo,更新您的模块,现在网站中有了一个商城板块,列出了预设(通过演示数据)的产品。 'security/ir.model.access.csv', 'templates.xml', 'views.xml', 'data.xml', ], # only loaded in demonstration mode 'demo': [
academy/data.xml<odoo> <record model="product.public.category" id="category_courses"> <field name="name">Courses</field> <field name="parent_id" ref="website_sale.categ_others"/> </record></odoo>
academy/demo.xml <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></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.csvid,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlinkaccess_academy_teachers,access_academy_teachers,model_academy_teachers,,1,0,0,0
academy/views.xml </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"/>
基于这个安装,一些课程现在在商城中可用了,即使它们还需要进行搜索。<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> -->
我们将更改第二件事情是使得产品分类的边栏按默认可见:自定义>产品分类,让您可以切换产品分类树的显示和隐藏(用于过滤主显示)。 </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> -->
这样,产品分类边栏会在Academy模块安装时自动启用。关键词:教程,官方,发文