odoo12從零開始:三、1)創建你的第一個應用模塊(module)

  • 2019 年 10 月 3 日
  • 筆記

前言

      以前,我一直都不知道為什麼好多框架的入門都是“hello world”開始,當我思前想後我要如何介紹odoo的model、record、template等繼承等高級特性時,發現在那之前便需要清楚地介紹什麼是模型(model),什麼是記錄(record),什麼是模板(template),以及他們到底是幹什麼用以及是怎麼用的?想要知道它們是怎麼用的,就得介紹odoo的一個應用模塊(module)的結構是什麼樣的。那麼了解一個應用結構最快的辦法,那就是我們自己去完成一個。Let’s do it!

Tips: 初學者容易搞混模塊(module)模型(model)。  模塊(module):是一個odoo應用,包含模型(models)、控制器(controllers)、視圖(views)、權限(ir.rule,ir.group, ir.model.access)、初始化數據(data)、報表(report)、嚮導(wizard)、靜態文件(static)等。  模型(model):是模塊(module)的一部分,是odoo的ORM的描述對象,它的工作是幫我們將內存中的對象反映為數據庫中的關係數據。

 模塊結構(Module Structure)

主要目錄:         data/ : demo和數據的xml文件       models/ : models定義  controllers/ : 包含controllers (HTTP路由等)        views/ : 包含視圖(views)和模板(templates)       static/ : 包含web資源, 分為css/, js/, img/, lib/等

其他可選目錄結構:       wizard/ : 嚮導,由瞬時模型(models.TransientModel)構成,以及嚮導的視圖(views)       report/ : 報表,包含含有sql的模型,XML文件等        tests/ : 測試代碼

項目結構示意圖

addons/模塊名/  |-- __init__.py  |-- __manifest__.py (描述文件)  |-- controllers/  |   |-- __init__.py  |   |-- main.py  |   |-- *****.py  |-- data/  |   |-- *****_data.xml  |   |-- *****_demo.xml  |-- models/  |   |-- __init__.py  |   |-- *****.py  |-- report/  |   |-- __init__.py  |   |-- *****_report.py  |   |-- *****_report_views.xml  |   |-- *****_reports.xml (report actions, paperformat, ...)  |   |-- *****_templates.xml (xml report templates)  |-- security/  |   |-- ir.model.access.csv  |   |-- *****_groups.xml  |   |-- *****_security.xml  |-- static/  |   |-- description/  |   |   |-- icon.png(模塊的icon)  |   |-- img/  |   |   |-- *****.png  |   |   |-- *****.jpg  |   |-- lib/  |   |   |-- external_lib/  |   |-- src/  |   |   |-- js/  |   |   |   |-- widget_a.js  |   |   |   |-- widget_b.js  |   |   |-- scss/  |   |   |   |-- widget_a.scss  |   |   |   |-- widget_b.scss  |   |   |-- xml/  |   |   |   |-- widget_a.xml  |   |   |   |-- widget_a.xml  |-- views/  |   |-- assets.xml  |   |-- **********.xml  |-- wizard/  |   |--*****.py  |   |--*****.xml

本節代碼

git clone -b v3.1 https://github.com/lingjiawen/odoo_project.git

開發模塊(module)

我們基於上一節的代碼版本(V2.1)中進行開發:

git clone -b v2.1 https://github.com/lingjiawen/odoo_project.git

我們先來嘗試仿照官方的hr模塊開發一個簡易版的員工模塊,包含員工基本信息,員工部門和員工職位管理。

1、創建用戶目錄

首先,我們在my_addons/下新建employee/目錄,在目錄下新建以下文件:

my_addons/employee/  |-- __init__.py  |-- __manifest__.py  |-- models/  |   |-- __init__.py  |-- views/

2、填寫描述文件__manifest__.py

# -*- coding: utf-8 -*-  {      'name': 'Employee',      'version': '12.0.1.0',      'summary': '對員工的基本信息,部門和職位進行管理',      'description': '''          員工管理模塊      ''',      'author': 'misterling',      'sequence': 15,      'category': 'Uncategorized',      'license': 'LGPL-3',      'depends': ['base'],      'data': [],      'demo': [],      'qweb': [],      'installable': True,      'application': True,      'auto_install': False,      # 'pre_init_hook': '',      # 'post_init_hook': '',      # 'uninstall_hook': '',  }

以上描述便是常用的配置項,我們來看看它們都代表着什麼:

          name: 模塊的標題         version: 版本號         summary: 模塊的子標題     description: 模塊的描述性文字          author: 作者        sequence: 模塊在apps中的排列的序號,影響展示順序。        category: 模塊的分類,在設置->用戶&公司->群組中"應用"字段中可以看到         license: 代表着你的開源協議。         depends: 依賴的模塊,在安裝當前模塊時,如果依賴模塊未安裝,將會自動安裝;升級依賴的模塊時,所有依賴它的模塊也將會跟着升級。
data: 加載XML文件。 demo: 加載demo文件。 qweb: 加載qweb template文件。 installable: 是否可以安裝。 application: 是否是應用,在應用列表中,被應用篩選隔離,好的開發習慣應該謹慎考慮是否是應用。 auto_install: 是否自動安裝,設為True的應用將在數據庫初始化時自動安裝 pre_init_hook: 顧名思義,模塊安裝前的鉤子,指定方法名即可 post_init_hook: 模塊安裝完成後的鉤子 uninstall_hook: 模塊卸載時的鉤子

Tips:我們在書寫python文件時,不用忘記在首部添加  # -*- coding: utf-8 -*-  以支持中文編碼

 3、創建員工對象

 我們在models/下面新建employee.py文件,編寫以下內容:

# -*- coding: utf-8 -*-  import base64  import logging    from odoo import api, fields, models  from odoo.modules.module import get_module_resource  from odoo import tools, _    _logger = logging.getLogger(__name__)    GENDER = [      ('male', u''),      ('female', u''),      ('other', u'其他')  ]    MARITAL = [      ('single', u'單身'),      ('married', '已婚'),      ('cohabitant', '合法同居'),      ('widower', '喪偶'),      ('divorced', '離婚')  ]      class Employee(models.Model):      _name = "ml.employee"      _description = '''          員工信息      '''        @api.model      def _default_image(self):          image_path = get_module_resource('hr', 'static/src/img', 'default_image.png')          return tools.image_resize_image_big(base64.b64encode(open(image_path, 'rb').read()))        name = fields.Char(string=u'姓名')        # image      image = fields.Binary(string=u"照片", default=_default_image, attachment=True, help=u"上傳員工照片,<1024x1024px")      image_medium = fields.Binary(string=u"中尺寸照片", attachment=True, help="128x128px照片")      image_small = fields.Binary(string=u"小尺寸照片", attachment=True, help="64x64px照片")        company_id = fields.Many2one('res.company', string=u'公司')        gender = fields.Selection(GENDER, string=u'性別')      country_id = fields.Many2one('res.country', string=u'國籍')      birthday = fields.Date(string=u'生日')      marital = fields.Selection(MARITAL, string=u'婚姻狀況', default='single')        # work      address = fields.Char(string=u'家庭住址')      mobile_phone = fields.Char(string=u'手機號碼')      work_email = fields.Char(string=u'工作郵箱')      leader_id = fields.Many2one('ml.employee', string=u'所屬上級')      subordinate_ids = fields.One2many('ml.employee', 'leader_id', string=u'下屬')        note = fields.Text(string=u'備註信息')        @api.model      def create(self, values):          tools.image_resize_images(values)          return super(Employee, self).create(values)        @api.multi      def write(self, values):          tools.image_resize_images(values)          return super(Employee, self).write(values)

我們一起來梳理一下類文件的主要內容:

from odoo import api, fields, models 引入 api, fields, models    1、class類
odoo的class繼承了models.Model類,是odoo最常用的模型類,其他的還有 models.TransientModel,瞬時模型,用於嚮導(wizard),系統會在一定時間後自動清除模型的記錄 models.AbstractModel,抽象模型,和抽象類是一樣的概念,系統不會為該模型建立數據庫表
2、內部標識 _name = "ml.employee",為odoo類的唯一標識,如果沒有指定_table屬性,那麼系統將會為該模型建立數據庫表名為ml_employee的數據表。 _description:主要為描述信息 3、使用的字段 odoo模型的字段使用fields.xxx來聲明。 1)Char:文本字段 2)Binary:二進制字段,通常用於圖片、附件等文件讀寫 3)Many2one:多對一關係字段,如: company_id = fields.Many2one('res.company', string=u'公司') 表現為多個員工可以對應同一個公司,'res.company'是odoo內置公司模型 4)Selection:列表選擇字段,第一個參數為元組列表,表示可選列表 5)Date: 日期控件字段 6)One2many:一對多關係字段,如: subordinate_ids = fields.One2many('ml.employee', 'leader_id', string=u'下屬') 表示一個員工可以有多個下屬 7)Text: 文本字段,在前端表現為textarea,char在前端表現為input 4、屬性 string:表示字段的顯示名稱 default:表示字段的默認值 attachment:binary字段的特有屬性,表現為是否以附件的形式存儲,設為True時,將會存儲到ir.attachment中 5、ORM 方法修飾器 @api.model:模型修飾器,相當於靜態方法,方法將為模型類共有,而不是每個實例。 @api.multi:對記錄集執行一些操作,方法的邏輯通常會包含對 self 的遍歷 6、方法 1)_default_image:我們獲取hr模塊目錄下的圖片,賦值給image字段作為默認值 2)create、write:重寫記錄的創建、編輯方法,使用odoo自帶的工具image_resize_images對image_medium,image_small進行賦值

寫完employee類之後,我們在與其同級的__init__.py中引入:

# -*- coding: utf-8 -*-    from . import employee

再在與models/目錄同級的__init__.py中引入models:

# -*- coding: utf-8 -*-    from . import models

到此,我們就已經創建好了employee的類模型,接着我們要為其寫視圖(views)

 4、編寫視圖

 我們先來看看odoo最常用的三種視圖:樹形(tree),表單(form),搜索(search),它們存儲於odoo內置的ir.ui.view模型中,其他還有圖形(graph)、透視表(pivot)、日曆(calendar)、圖標(diagram)、甘特圖(gantt)、看板(kanban)、QWEB、活動(activity),是odoo的最主要的頁面展現形式。

1)tree視圖

 tree視圖為模型記錄(record)的列表展示形式

2)form視圖

 form視圖為表單展現形式,主要用於odoo記錄的創建,編輯。

3)search視圖

search視圖主要用於在tree、kanban等視圖中進行搜索、過濾、分組記錄以方便查看。

我們為我們的員工模型書寫這三種視圖:

新建views/employee.xml文件,加入odoo data標籤:

<?xml version="1.0" encoding="utf-8"?>  <odoo>      <data>      </data>  </odoo>

先在data內增加一個form視圖:

<record id="view_ml_employee_form" model="ir.ui.view">      <field name="name">員工信息表單</field>      <field name="model">ml.employee</field>      <field name="arch" type="xml">          <form string="員工信息">              <sheet>                  <field name="image" widget='image' class="oe_avatar"                         options='{"preview_image":"image_medium"}'/>                  <div class="oe_title">                      <label for="name" class="oe_edit_only"/>                      <h1>                          <field name="name" placeholder="員工姓名" required="True"/>                      </h1>                  </div>                  <notebook>                      <page string="員工信息">                          <group>                              <group string="基本信息">                                  <field name="gender" required="True"/>                                  <field name="country_id"/>                                  <field name="birthday"/>                                  <field name="marital"/>                              </group>                              <group string="工作信息">                                  <field name="company_id" options="{'no_open': True, 'no_create': True}" groups="base.group_multi_company"/>                                  <field name="address"/>                                  <field name="mobile_phone" widget="phone"/>                                  <field name="work_email" widget="email"/>                                  <field name="leader_id" options="{'no_open': True, 'no_create': True}"/>                              </group>                          </group>                      </page>                      <page string="下屬信息">                          <field name="subordinate_ids">                              <tree editable="bottom">                                  <field name="name" attrs="{'required': True}"/>                                  <field name="gender" required="True"/>                                  <field name="country_id"/>                                  <field name="mobile_phone"/>                                  <field name="work_email"/>                              </tree>                          </field>                      </page>                  </notebook>              </sheet>          </form>      </field>  </record> 

 我們來看看form表單的寫法:

寫一個form表單,實質上在為模型ir.ui.view增加一條記錄,odoo中為模型增加一條記錄可以使用record標籤,我們為它取了唯一的id:view_ml_employee_form(我們約定,記錄的書寫使用view_模型名_form/tree的命名方式),然後使用record內的model屬性指定增加的記錄屬於ir.ui.view模型。

我們先使用field標籤插入name和model,

<field name="name">員工信息表單</field>
<field name="model">ml.employee</field>

代表我們是為ml.employee模型書寫的form視圖

我們在系統設置中打開“開發者模式”,然後打開設置->技術->用戶界面->視圖,可以看到系統中現在已有的記錄(完成開發並升級後):

我們使用

<field name="arch" type="xml">  </field>

插入form內容。

其中:

1、使用<form></form>標籤包裹表示記錄類型為form視圖  2、使用<field name="屬性名" />的方式顯示字段  3、<notebook>          <page>          </page>          <page>          </page>          ……      </notebook>  為翻頁標籤  4、widget='image'為顯示類型為圖片  5、required="True"為必填,常用的還有invisible、readonly等  6、需要使用group包裹field以正常顯示字段的string值

One2many字段有特定的寫法:

<field name="subordinate_ids">      <tree editable="bottom">          <field name="name" attrs="{'required': True}"/>          <field name="gender" required="True"/>          <field name="country_id"/>          <field name="mobile_phone"/>          <field name="work_email"/>      </tree>  </field>

字段內嵌tree視圖來自定義顯示方式,editable=”bottom”表示不彈出新窗口來創建明細記錄。

細心的朋友可能看到必填有 required=True 和 attrs=”{‘required’: True}”兩種寫法,事實上,invisible, readonly也有這兩種寫法。

他們的區別在於:

required=True:這個寫法是死的,在視圖加載時就已經確定。  attrs="{"required": [('name', '!=', False)]}: 這種寫法可以書寫domain來過濾(上面的寫法也可以)。最重要的是,它會隨着name的變化來動態改變required的值。

更多詳細的介紹我們將會在後面views的專章介紹,這裡只要了解大概就可以了。

options="{'no_open': True, 'no_create': True}"

其主要作用在對於Many2one字段,不允許其打開和新建它的專有視圖。

Tips:  我們約定:many2one字段的命名使用xxx_id,如leader_id;          One2many字段的命名我們使用xxx_ids,如subordinate_ids;

我們再為它書寫tree視圖:

<record id="view_ml_employee_tree" model="ir.ui.view">      <field name="name">員工信息列表</field>      <field name="model">ml.employee</field>      <field name="arch" type="xml">          <tree string="員工信息">              <field name="name"/>              <field name="company_id"/>              <field name="gender"/>              <field name="country_id"/>              <field name="mobile_phone"/>              <field name="work_email"/>              <field name="leader_id"/>          </tree>      </field>  </record> 

Tree視圖相對比較簡單:

1、<tree></tree>包裹表示為tree視圖  2、羅列字段以確定列表的顯示字段以及顯示順序

我們再為其添加Search視圖:

<record id="view_ml_employee_filter" model="ir.ui.view">      <field name="name">員工搜索視圖</field>      <field name="model">ml.employee</field>      <field name="arch" type="xml">          <search string="員工">              <!--用於搜索的字段-->              <field name="name" string="員工"                     filter_domain="['|',('work_email','ilike',self),('name','ilike',self)]"/>              <field name="gender" string="性別"/>              <separator/>              <!--定義好的過濾器-->              <filter string="男員工" name="gender_male"                      domain="[('gender', '=', 'male')]"/>              <filter string="女員工" name="gender_female"                      domain="[('gender', '=', 'female')]"/>              <separator/>              <!--分組-->              <group expand="0" string="分組">                  <filter name="group_leader" string="領導" domain="[]" context="{'group_by':'leader_id'}"/>                  <filter name="group_company" string="Company" domain="[]" context="{'group_by':'company_id'}"                          groups="base.group_multi_company"/>              </group>          </search>      </field>  </record>

我們可以看到:

1、<search></search>包裹表示為search視圖  2、<field name="XXX" />聲明可以用於搜索的字段  3、<filter string="XXX" name="XXX" domain="XXX" />表示系統定義的過濾器  4、使用<group></group>包裹filter可以進行分組

實際效果如下:

1)搜索

2)過濾器

 

 3)分組

 

 5、編寫動作和菜單

我們寫好了tree、form和search視圖,我們需要編寫動作和菜單來定義行為:

點擊菜單->觸髮菜單對應的action動作->展示action中綁定的視圖

 

我們繼續在data內增加:

<record model="ir.actions.act_window" id="view_ml_employee_action">      <field name="name">員工信息</field>      <field name="res_model">ml.employee</field>      <field name="view_type">form</field>      <field name="view_mode">tree,form</field>      <field name="view_id" ref="view_ml_employee_tree"/>      <field name="search_view_id" ref="view_ml_employee_filter"/>  </record>

我們可以看到,動作對應的系統model為ir.action.act_window,我們一樣可以在技術->動作->動作下找到我們定義的動作。

      view_mode:表示我們需要展示的視圖(有先後順序),tree視圖在最前面,我們觸發動作時首先展示的就是tree視圖;

           view_id:表示我們引用的視圖ref=”view_ml_employee_tree”,也就是我們在前面定義的tree視圖;

search_view_id:表示我們引用的過濾器為”view_ml_employee_filter”;

根據上面需要引用tree和search我們不難推斷,可以為model定義多個tree、form和search視圖,通過不同的action,綁定不同的菜單,可以觸發同一模型不同的展示視圖。

 

最後,我們為action添加一個菜單,我們習慣於將菜單使用單獨的文件保存,所以我們新建views/menu.xml文件,書寫下列內容:

<?xml version="1.0" encoding="utf-8"?>  <odoo>      <data>          <!--一級菜單-->          <menuitem                  id="menu_employee_root"                  name="員工"                  web_icon="hr,static/description/icon.png"                  sequence="1"/>          <!--二級菜單 -->          <menuitem                  id="menu_employee_info"                  name="員工信息"                  parent="menu_employee_root"                  sequence="1"/>          <!--三級菜單 -->          <menuitem                  id="menu_view_ml_employee_tree"                  name="員工檔案"                  action="view_ml_employee_action"                  parent="menu_employee_info"                  sequence="1"/>      </data>  </odoo>

可以看到,我們使用menuitem標籤定義菜單:

web_icon::一級菜單特有屬性,表示展示的圖標,這裡我們借用hr模塊的圖標

sequence:菜單的展示順序

parent:上級菜單,沒有定義則為一級菜單

action:菜單對應的動作,我們在三級菜單中添加我們剛才編寫的action:view_ml_employee_action

 6、引用並安裝模塊

在__manifest__.py->data中引用views:

'data': [          'views/employee.xml',          'views/menu.xml',  ],

然後我們重啟服務器,再打開“開發者模式”,在應用頁面中刷新本地列表

 再搜索employee,點擊安裝

 安裝之後我們就可以看到菜單了:

 

 撒花!!!等等!看不到?為什麼呢!

Tips:Odoo12之前,admin用戶就是root用戶。Odoo12新增了root用戶,在用戶列表中不顯示,只在框架需要使用sudo增加權限時才使用。admin依然可以登入系統並擁有所有功能的訪問權限,但不再能繞過訪問限制

那我們有沒有辦法進入root用戶模式呢?有的:

首先,我們登入admin用戶,然後”激活開發者模式”,右上角進行”登出”,你會發現登錄頁面多了一個”以超級用戶登錄”的方式

 

點擊登錄進去發現右上角有花紋,代表已經進入root模式,此時發現已經可以看到”員工模塊”信息。

但是我們不能每次都通過這種方式來訪問,而且其他用戶也沒有辦法對這個頁面進行訪問,所以我們要為它寫訪問權限:

新建employee/security目錄,在security目錄下新建ir.model.access.csv文件,增加內容:

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink  access_ml_employee,員工檔案權限,employee.model_ml_employee,,1,1,1,1

這樣查看比較混亂,我們再pycharm文件內->右鍵->edit as table->確定:

id:唯一標識  name:名稱  model_id:id:對應model,使用model_+下劃線格式模型_name作為標識  group_id:id:所屬群組信息,這裡我們置空  perm_read、perm_write、perm_create、perm_unlink分別為讀、寫、創、刪四個權限

在manifest中加上引用:

'data': [      'security/ir.model.access.csv',        'views/employee.xml',      'views/menu.xml',  ]

在應用界面中升級應用,ok~

 參考

1、Odoo官網引導: http://www.odoo.com/documentation/12.0/reference/guidelines.html

2、《Odoo12 Development Essentials –Fourth Edition》 –Daniel Reis

聲明

原文來自於博客園(https://www.cnblogs.com/ljwTiey/p/11486885.html)

轉載請註明文章出處,文章如有任何版權問題,請聯繫作者刪除。

有任何問題,聯繫郵箱:[email protected]