一篇文章帶你入門odoo


  1. 激活開發者模式

    • 點擊設置(點下左上角的四個小方塊, 就能看到)
    • 點擊頁面右側的激活開發者模式
  2. 成為超級用戶

    • 點擊右上方開發者工具(小蟲子按鈕)
    • 點擊成為超級用戶
  3. 創建應用

    • python odoo-bin scaffold example addons
      • python odoo-bin scaffold 模型名稱 放置它的位置

      • 執行后會發現在odoo-12.0/addons里面有個新建的文件夾example, 里面會包含__init__.py __manifest__.py controllers demo models security views這幾個文件夾

      • 應用目錄

        • controllers
          • 控制器 (HTTP路徑)
        • data
          • 演示和數據XML
        • doc
          • 模型說明
        • models
          • 定義模型
        • report
          • 報告
        • security
          • 權限管理
        • i18n
          • 翻譯
        • views
          • 視圖和模型
        • static
          • CSS
          • JS
          • IMG
          • LIB
          • ...
        • tests
          • 存放 python 和 yml 測試用例
        • wizard
          • 放臨時的 model 和視圖
        \_\_manifest\_\_.py
        # -*- coding: utf-8 -*-
        {
            # 模型名
            'name': "example",
        
            # 摘要
            'summary': """
                Short (1 phrase/line) summary of the module's purpose, used as
                subtitle on modules listing or apps.openerp.com""",
            # 介紹
            'description': """
                Long description of module's purpose
            """,
        
            # 作者
            'author': "My Company",
        
            # 網址
            # 'website': "http://www.yourcompany.com",
        
            # Categories can be used to filter modules in modules listing
            # Check https://github.com/odoo/odoo/blob/12.0/odoo/addons/base/data/ir_module_category_data.xml
            # for the full list
        
            # 類別
            'category': 'Uncategorized',
        
            # 版本號
            'version': '0.1',
        
            # any module necessary for this one to work correctly
            # 依賴
            'depends': ['base'],
        
            # always loaded
            # 數據文件
            'data': [
                # 'security/ir.model.access.csv',
                'views/views.xml',
                'views/templates.xml',
            ],
            # only loaded in demonstration mode
                
            # 演示文件
            'demo': [
                'demo/demo.xml',
            ],
        }
        
  4. 安裝應用

    1. 重啟odoo服務
    2. 點擊刷新本地模型列表
    3. 彈出窗口點擊更新按鈕
    4. 刪除搜索框內容,然后輸入example
    5. 點擊安裝
      • 以后執行步驟4時, 點擊三個小點點. 然后點擊升級按鈕
  5. 新建模型

    修改addons/example/models/models.py
    # -*- coding: utf-8 -*-
    import datetime
    
    from odoo import models, fields, api
    
    
    class example(models.Model):
        _name = 'example.example'
        name = fields.Char()
        active = fields.Boolean(default=True, string='歸檔', required=True)  # 系統保留變量, False時不在主頁顯示,需要篩選改為True才能顯示
        price = fields.Float()
        note = fields.Text()
        content = fields.Html(readonly=True)
        my_datetime = fields.Datetime(default=fields.Datetime.now)
        # my_date = fields.Date(default=fields.Date.today)
        my_date = fields.Date(default=lambda self: datetime.date.today() + datetime.timedelta(days=4))
        select = fields.Selection([
            ('1', 'em'),
            ('2', 'emm'),
            ('3', 'emmm'),
        ])
        em1_mo = fields.Many2one(comodel_name='example.example1')
        em1_om = fields.One2many(comodel_name='example.example1', inverse_name='em_mo')
        em1_mm = fields.Many2many(comodel_name='example.example1', column1='name', column2='fuck_id')
        my_reference = fields.Reference(selection='_select_objects')
    
        @api.model
        def _select_objects(self):
            records = self.env['ir.model'].search([])
            return [(record.model, record.name) for record in records] + [('', '')]
    
    
    class em1(models.Model):
        _name = 'example.example1'
        fuck_id = fields.Integer()
        em_mo = fields.Many2one('example.example')
    
    
    知識內容
    • 模型的字段
      • 系統字段

        • _name
          • 必需的
          • 定義了Odoo系統中模型的名稱
        • _description
          • 為模型添加更為友好的描述,更新后在Settings下可發現相應的變化(需開啟調試模式Database Structure>Models)
        • _order
          • 默認情況下Odoo使用內置的id進行排序,可以通過逗號分隔指定多個字段進行排序,desc表示降序,僅能使用數據庫中存儲的字段排序,外部計算后的字段不適用,_order有些類似SQL語句中的ORDER BY,但無法使用NULL FIRST之類的語句
        • 字段共同屬性
          • string(unicode, 默認:字段名稱)
            • UI中字段的標簽(用戶可見)
          • required(bool默認:False)
            • 如果True該字段不能為空, 則它必須具有默認值或在創建記錄時始終給定值
          • help(unicode默認:'')
            • 長格式, 在UI中為用戶提供幫助工具提示
          • index(bool默認:False)
            • 請求Odoo 在列上創建數據庫索引
          • readonly(bool默認:False)
            • 是否只讀
      • Model字段類型

        • 字段共同屬性

          • string(unicode, 默認:字段名稱)
            • UI中字段的標簽(用戶可見)
          • required(bool默認:False)
            • 如果True該字段不能為空, 則它必須具有默認值或在創建記錄時始終給定值
          • help(unicode默認:'')
            • 長格式, 在UI中為用戶提供幫助工具提示
          • index(bool默認:False)
            • 請求Odoo 在列上創建數據庫索引
          • readonly(bool默認:False)
            • 是否只讀
        • 常用字段屬性

          • default
            • 默認值
        • Binary

          • Binary字段用於存儲二進制文件,如圖片或文檔
          • a = fields.Binary()
        • Float

          • Float用於存儲數值,其精度可以通過數字長度和小數長度一對值來進行指定
          a = fields.Float(
              string='float',
              digits=(14, 4), # Optional precision (total, decimals)
              )
          
        • Boolean

          • Boolean字段用於存儲True/False布爾值
          • a = fields.Boolean()
        • Integer

          • Integer即為整型
          • a = fields.Integer()
        • Char

          • Char用於字符串
          • a = fields.Char(string='aa', required=True)
        • Date

          • Date字段用於存儲日期,ORM中以字符串格式對其進行處理,但以日期形式存放在數據庫中,該格式在odoo.fileds.DATE_FORMAT中定義
          • my_date = fields.Date(default=fields.Date.today)
          • my_date = fields.Date(default=lambda self: datetime.date.today() + datetime.timedelta(days=4))
        • Datetime

          • Datetime用於存儲日期時間,在數據庫中以UTC無時區時間(naive)存放,ORM中以字符串和UTC時間表示,該格式在odoo.fields.DATETIME_FORMAT中定義
          • my_datetime = fields.Datetime(default=fields.Datetime.now)
        • Text

          • Text用於多行字符串
          • notes = fields.Text()
        • Monetary

          • 貨幣
          • a=fields.Monetary()
        • Html

          • Used to store HTML, provides an HTML widget.
          • Html類似text字段,但一般用於存儲富文本格式的HTML
          • strip_style=True:清除所有樣式元素
          • strip_class=True:清除類屬性
          • description = fields.Html()
        • Selection

          • Store Text in database but propose a selection widget. It induces no selection constraint in database. Selection must be set as a list of tuples or a callable that returns a list of tuples
          • Selection用於選擇列表,由值和描述對組成,選擇的值將存儲在數據庫中,可以為字符串或整型,描述默認可翻譯,雖然整型的值看似簡潔,但注意Odoo會把0解析為未設置(unset),因而當存儲值為0時不會顯示描述
          • index: Tells Odoo to index for faster searches replaces the select kwarg
          select = fields.Selection([
              ('1', 'em'),
              ('2', 'emm'),
              ('3', 'emmm'),
          ], required=True, default='1')
          
        • Many2one

          • Store a relation against a co-model
          • comodel_name指定綁定多對一的模型名, 即類名
          • em1_mo = fields.Many2one(comodel_name='example.example1')
        • Many2many

          • Store a relation against many2many rows of co-model
          • Many2many會新建一個中間表, relation指定中間表的表名
          • 例:
            _name = 'emm.a'
            e = Many2many(
                comodel_name='emmm.b',   # 關聯的表
                relation='emmm_a_b_rel', # 可選, 中間表名
                column1='a_id',          # 當前表的字段名
                column2='b_id',          # 關聯的其他表的字段名
                string='Tags') 
            
        • One2many

          • Store a relation against many rows of co-model

          • 新建one2many前需要先寫many2one, 並且數據庫的外鍵會建立在many2one上

          • comodel_name: 指定綁定一對多的模型名, 即類名

          • inverse_name: 指定綁定一對多的模型的Many2one字段的名

          • domain

            • 過濾, 例:domain=[('id','=',1)]
            class em(models.Model):
                _name = 'example.example'
                em1_om = fields.One2many(comodel_name='example.example1', inverse_name='em_mo')
            
            class em1(models.Model):
                _name = 'example.example1'
                em_mo = fields.Many2one('example.example')
            
        • Reference

          • Store an arbitrary reference to a model and a row
          • 事先不能決定關聯的目標模型時, 這種情況需要使用reference將目標模型的選擇權留給用戶
          my_reference = fields.Reference(selection='_select_objects')
          
          @api.model
          def _select_objects(self):
              records = self.env['ir.model'].search([])
              return [(record.model, record.name) for record in records] + [('', '')]
          
      • 保留字段

        • Odoo在所有模型中都創建了以下幾個字段,這些字段由系統管理,是系統保留的字段, 用戶不應定義
          • id(Id)
            • 模型中記錄的唯一標識符
          • create_date(Datetime)
            • 記錄的創建日期
          • create_uid(Many2one)
            • 創建記錄的用戶
          • write_date(Datetime)
            • 記錄的最后修改日期
          • write_uid(Many2one)
            • 上次修改記錄的用戶
      • Method and decorator

        • @api.returns
          • This decorator guaranties unity of returned value. It will return a RecordSet of specified model based on original returned value:
          @api.returns('res.partner')
          def afun(self):
              ...
              return x  # a RecordSet
          
        • @api.one
          • This decorator loops automatically on Records of RecordSet for you. Self is redefined as current record:
          @api.one
          def afun(self):
              self.name = 'toto'
          
        • @api.multi
          • Self will be the current RecordSet without iteration. It is the default behavior:
          @api.multi
          def afun(self):
              len(self)
          
        • @api.model
          • This decorator will convert old API calls to decorated function to new API signature. It allows to be polite when migrating code.
          @api.model
          def afun(self):
              pass
          
        • @api.constrains
          • This decorator will ensure that decorated function will be called on create, write, unlink operation. If a constraint is met the function should raise a openerp.exceptions.Warning with appropriate message.
        • @api.depends
          • This decorator will trigger the call to the decorated function if any of the fields specified in the decorator is altered by ORM or changed in the form
          @api.depends('name', 'an_other_field')
          def afun(self):
              pass
          
        • @api.onchange
          • This decorator will trigger the call to the decorated function if any of the fields specified in the decorator is changed in the form
          @api.onchange('fieldx')
          def do_stuff(self):
              if self.fieldx == x:
                  self.fieldy = 'toto'
          
        • @api.noguess
          • This decorator prevent new API decorators to alter the output of a method
  6. 升級應用

    1. 重啟odoo服務
    2. 在搜索框輸入example
    3. 點擊升級
  7. 修改數據文件

    修改addons/example/views/views.xml文件
    <odoo>
        <data>
            <!-- explicit list view definition -->
            <!--選擇顯示哪些字段, 默認是name,即ui-->
            <record model="ir.ui.view" id="example.list">
                <field name="name">example模型表頭</field>
                <field name="model">example.example</field>
                <field name="arch" type="xml">
                    <tree>
                        <field name="name"/>
                        <field name="active"/>
                        <field name="price"/>
                    </tree>
                </field>
            </record>
    
    
            <!-- actions opening views on models -->
            <!--選擇哪些功能動作即action-->
            <!--res_model是目標模塊的標識符 -->
            <record model="ir.actions.act_window" id="example.action_window">
                <field name="name">example act_window</field>
                <field name="res_model">example.example</field>
                <!--
                tree, 顯示數據
                form, 新建數據
                默認為tree
                -->
                <field name="view_mode">tree,form</field>
            </record>
    
    
            <!-- server action to the one above -->
            <!--執行py代碼-->
            <record model="ir.actions.server" id="example.action_server">
                <field name="name">example server</field>
                <field name="model_id" ref="model_example_example"/>
                <field name="state">code</field>
                <!--
                <field name="code">里面包裹的是py代碼
                action將作為下一個要執行的操作返回給客戶端
                -->
                <field name="code">
                    action = {
                    "type": "ir.actions.act_window",
                    "view_mode": "tree,form",
                    "res_model": "example.example",
                    }
                </field>
            </record>
    
    
            <!-- Top menu item -->
            <!--頂部菜單-->
            <menuitem name="example" id="example.menu_root"/>
    
            <!-- menu categories -->
            <!--分菜單-->
            <menuitem name="Menu 1" id="example.menu_1" parent="example.menu_root"/>
            <menuitem name="Menu 2" id="example.menu_2" parent="example.menu_root"/>
    
    
            <!-- actions -->
            <!--菜單綁定動作-->
            <menuitem name="List" id="example.menu_1_list" parent="example.menu_1"
                    action="example.action_window"/>
            <menuitem name="Server to list" id="example" parent="example.menu_2"
                    action="example.action_server"/>
    
        </data>
    </odoo>
    

    重復步驟4 升級應用

    知識內容
    • 數據文件僅在安裝或更新模塊時才加載數據文件的內容

    • 模塊的數據通過帶有<record>元素的數據文件, XML文件聲明.每個<record>元素都創建或更新數據庫記錄

    • 數據文件必須在要manifest文件中聲明數據文件, 它們可以在data列表(始終加載)或demo列表中聲明(僅在演示模式下加載).

    • 屬性

      • model
        • 記錄的Odoo模塊的名稱
      • id
        • 一個外部標識符, 用於被引用
      • <field>
        • name標識字段名稱
        • <field>的innnerText是<field>的值
    • menuitem

      • 必須先聲明相應的Action, 因為數據文件按順序執行, 在id創建菜單之前, Action必須存在於數據庫中
      • 在創建完菜單后必須成為超級用戶才能正常顯示新建的菜單
      • menuitem 只有綁定action, 或子menuitem綁定了action才能顯示出來
      • 屬性
        • id: 定義唯一標記
        • action: 綁定動作
        • parent: 父菜單
        • sequence: 優先級, 數字越小優先級越高, 顯示越靠前,最小為0
        • groups: 綁定權限
        • name: 菜單名稱
    • view

      • 視圖定義了模塊記錄的顯示方式(Views define the way the records of a model are displayed).每種類型的視圖代表一種可視化模式(記錄列表, 其聚合圖, ......).可以通過類型(例如合作伙伴列表)或特別是通過其ID 來一般性地請求視圖.對於通用請求, 將使用具有正確類型和最低優先級的視圖(因此每種類型的最低優先級視圖是該類型的默認視圖).

      • 對於view 中的<record>的id, 會被存到數據庫中, 而且當你修改<record>的model, 然后再次執行, 則<record>的model仍為修改前的model, 不報錯, 且正常使用

        • 解決方法:
          • 修改<record>的id
          • 或將<record>刪除, 重啟服務,升級模塊, 然后再寫<record>
  8. 自定義添加數據的表單

    修改addons/example/models/models.py
    # -*- coding: utf-8 -*-
    import datetime
    
    from odoo import models, fields, api
    
    
    class example(models.Model):
        _name = 'example.example'
        name = fields.Char()
        active = fields.Boolean(default=True, string='歸檔',required=True)  # 系統保留變量, False時不在主頁顯示,需要篩選改為True才能顯示
        price = fields.Float()
        note = fields.Text()
        content = fields.Html(readonly=True)
        my_datetime = fields.Datetime(default=fields.Datetime.now)
        # my_date = fields.Date(default=fields.Date.today)
        my_date = fields.Date(default=lambda self: datetime.date.today() + datetime.timedelta(days=4))
        select = fields.Selection([
            ('1', 'em'),
            ('2', 'emm'),
            ('3', 'emmm'),
        ])
        em1_mo = fields.Many2one(comodel_name='example.example1')
        em1_om = fields.One2many(comodel_name='example.example1', inverse_name='em_mo')
        em1_mm = fields.Many2many(comodel_name='example.example1', column1='name', column2='fuck_id')
        my_reference = fields.Reference(selection='_select_objects')
    
        @api.model
        def _select_objects(self):
            records = self.env['ir.model'].search([])
            return [(record.model, record.name) for record in records] + [('', '')]
    
        # 添加狀態值和, 按鈕事件
        state = fields.Selection(
            [('1', '狀態1'), ('2', '狀態2'), ('3', '狀態3')],
            readonly=True,
            default='1'
        )
        """
        [(狀態值, 狀態條中顯示的內容), ('1', '狀態1'),...]
        """
    
        def button1(self):
            return self.write({'state': '1'})
    
        def button2(self):
            return self.write({'state': '2'})
    
        def button3(self):
            return self.write({'state': '3'})
    
    class em1(models.Model):
        _name = 'example.example1'
        fuck_id = fields.Integer()
        em_mo = fields.Many2one('example.example')
    
    
    修改addons/example/views/views.xml文件
    <odoo>
        <data>
            <record model="ir.ui.view" id="example.list">
                <field name="name">列表顯示字段</field>
                <field name="model">example.example</field>
                <field name="arch" type="xml">
                    <tree>
                        <field name="name"/>
                        <field name="active"/>
                        <field name="price"/>
                        <field name="note"/>
                        <field name="content"/>
                        <field name="my_date"/>
                        <field name="my_datetime"/>
                        <field name="select"/>
                        <field name="em1_mo"/>
                        <field name="em1_om"/>
                        <field name="em1_mm"/>
                        <field name="my_reference"/>
                    </tree>
                </field>
            </record>
    
            <!--動作-->
            <record id="example.root_menu_action_view" model="ir.actions.act_window">
                <field name="type">ir.actions.act_window</field>
                <field name="name">我會被顯示出來</field>
                <field name="res_model">example.example</field>
            </record>
    
            <!--自定義form表單-->
            <record id="example.example_form_view" model="ir.ui.view">
                <field name="model">example.example</field>
                <field name="arch" type="xml">
                    <form>
                        <header>
                            <!--三個按鈕, 一個狀態條-->
                            <!--
                            - button
                                - name: 點擊時執行的函數名
                                - string: 按鈕顯示內容
                                - states: 當狀態值為多少時, 該按鈕可見, 不設置則永遠可見
                            - <field name="state" widget="statusbar"/>
                                -  statusbar_visible: 根據state值確定哪些狀態在狀態條上顯示
                            -->
    
                            <button states="1" name="button2" string="當前狀態值為1, 點擊設置為2" class="oe_highlight" type="object"/>
                            <button states="1" name="button3" string="當前狀態值為1, 點擊設置為3" class="oe_highlight" type="object"/>
                            <button states="2" name="button3" string="當前狀態值為2, 點擊設置為3" class="oe_highlight" type="object"/>
                            <button states="3" name="button1" string="當前狀態值為3, 點擊設置為1" class="oe_highlight" type="object"/>
                            <field name="state" widget="statusbar" statusbar_visible="1,2,3"/>
    
                        </header>
                        <sheet>
                            <group string="group1">
                                <field name="name"/>
                                <field name="active"/>
                                <field name="price" readonly="1"/>
                            </group>
                            <group string="group2">
                                <field name="my_datetime" attrs="{'invisible': [('active', '=', False)]}"/>
                                <!--
                                此處的required的優先級大於models中的required, 但是提交時會彈錯
                                -->
                                <field name="my_date" required="1"/>
                                <field name="select" required="0"/>
                            </group>
                            <group string="group3">
                                <field name="em1_mo"/>
                                <field name="em1_om"/>
                                <field name="em1_mm"/>
                                <field name="my_reference"/>
    
                            </group>
                            <notebook string="notebook1">
                                <page string="page1">
                                    <field name="note"/>
    
                                </page>
                                <page string="page1">
                                    <field name="content"/>
                                </page>
                            </notebook>
                        </sheet>
                    </form>
                </field>
            </record>
    
            <menuitem id="example.root_menu" name="example" action="example.root_menu_action_view" sequence="1"/>
        </data>
    </odoo>
    
    重復步驟4: 升級應用 知識內容
    • model="ir.ui.view"的recoder 不需要綁定任何只要在xml文件中出現既能正常顯示

    • 視圖被聲明為ir.ui.view的Model的record.視圖類型由arch字段的根元素聲明

    • 表單視圖

      • 屬性
        • create="0": 不可新建
        • edit="0": 編輯
        • delete="0": 刪除
      • field屬性
        • widget
          • statusbar
            • 頭部狀態條標簽
          • email
            • 電子郵件地址標簽
          • selection
            • 下拉選擇標簽
          • mail_followers
            • 關注者標簽
          • mail_thread
            • 消息標簽
          • progressbar
            • 進度條,按百分比標簽
          • one2many_list
            • 一對多列表標簽
          • many2many_tags
            • 多對多顯示標簽
          • url
            • 網站鏈接標簽
          • image
            • 圖片標簽
          • many2many_kanban
            • 看版標簽
          • handler
            • 觸發標簽
          • radio
            • 單選標簽
          • char_domain
            • 字符域標簽
          • monetary
            • 價格(和精度位數相關)標簽
          • float_time
            • 單精度時間標簽
          • html
            • html相關標簽
          • pad
            • pad顯示相關標簽
          • date
            • 日期標簽
          • monetary
            • 金額標簽
          • text
            • 文本標簽
          • sparkline_bar
            • 燃盡標簽
          • checkbox
            • 復選框標簽
          • reference
            • 關聯標簽
        • required
          • 必填
        • readonly
          • 只讀
        • invisible
          • 不可見
        • 根據條件變化
          • name='123'invisible="1"
          • active=Truerequired="1"
          • 當前字段不是'many2many', 'many2one'狀態時readonly="1"
          attrs="{
              'invisible':[('name','=','123')],
              'required':[('active','=', True)],
              'readonly':[('ttype','not in', ['many2many', 'many2one'])]    
              }"
          
        • 過濾one2many, many2many, 后面的many
          • domain
          • domain="[('id','=',1)]"
      表單視圖還可以使用純HTML來實現更靈活的布局
      <form string="Idea Form">
          <header>
              <button string="Confirm" type="object" name="action_confirm" states="draft" 
              class="oe_highlight" />
              <button string="Mark as done" type="object" name="action_done" 
              states="confirmed" class="oe_highlight"/>
              <button string="Reset to draft" type="object" name="action_draft" 
              states="confirmed,done" />
              <field name="state" widget="statusbar"/>
          </header>
          <sheet>
              <div class="oe_title">
                  <label for="name" class="oe_edit_only" string="Idea Name" />
                  <h1><field name="name" /></h1>
              </div>
              <separator string="General" colspan="2" />
              <group colspan="2" col="2">
                  <field name="description" placeholder="Idea description..." />
              </group>
          </sheet>
      </form>
      
    • 樹視圖

      樹視圖(也稱為列表視圖)以表格形式顯示記錄.他的根元素是<tree>.最簡單的樹形視圖, 即只需列出要在表中顯示的所有字段(每個字段作為列)

      • 屬性

        • create="0": 不可新建
        • edit="0": 編輯
        • delete="0": 刪除
        <tree string="Idea list">
            <field name="name"/>
            <field name="inventor_id"/>
        </tree>
        
    • 搜索視圖

      搜索視圖通過列表視圖(以及其他聚合視圖)自定義關聯的搜索字段.他的根元素是<search>, 他包含的字段, 定義了哪些時用於搜索的字段

      <search>
          <field name="name"/>
          <field name="inventor_id"/>
      </search>
      
          </field>
      </record>
      
      <record model="ir.ui.view" id="course_search_view">
          <field name="name">course.search</field>
          <field name="model">openacademy.course</field>
          <field name="arch" type="xml">
              <search>
                  <field name="name"/>
                  <field name="description"/>
              </search>
          </field>
      </record>
      
      • 如果模塊不存在搜索視圖, 則Odoo會生成僅允許在該name字段上搜索的視圖.
  9. 權限管理

    添加文件addons/em/security/security.xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <odoo>
        <record id="example.example_category" model="ir.module.category">
            <field name="name">example.example_category</field>
            <field name="sequence" eval="1"/>
        </record>
        <record id="example.example_groups_a" model="res.groups">
            <field name="name">example.example_groups_a</field>
            <field name="category_id" ref="example.example_category"/>
            <field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
        </record>
        <record id="example.example_groups_b" model="res.groups">
            <field name="name">example.example_groups_b</field>
            <field name="category_id" ref="example.example_category"/>
            <!--example.example_groups_a和example.example_groups_b, 只能選一個-->
            <field name="implied_ids" eval="[(4, ref('example.example_groups_a'))]"/>
    
            <!--example.example_groups_a和example.example_groups_b, 可以同時選-->
            <!--<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>-->
        </record>
    </odoo>
    
    修改addons/example/\_\_manifest\_\_.py
    ...
    # always loaded
    # 數據文件
    'data': [
        'security/security.xml',
        'views/views.xml',
        'views/templates.xml',
    ],
    ...
    
    • 兩種方法設置字段指定用戶組可見

      法一. 修改addons/example/models/models.py
      ...
      # 設置example.example_groups_b組的用戶可見
      select = fields.Selection([
          ('1', 'em'),
          ('2', 'emm'),
          ('3', 'emmm'),
      ], groups="example.example_groups_b")
      ...
      

      法二. 修改addons/example/views/views.xml

      ...
      <field name="select" required="0" groups="example.example_groups_b"/>
      ...
      
      個人推薦法一
      <record model="ir.actions.act_window" id="example.action_window">
          <field name="name">example act_window</field>
          <field name="res_model">example.example</field>
          <field name="view_mode">tree,form</field>
          <!-- 在model中設置groups后, 會自動權限管理 -->
      </record>
      
    • 重復步驟4: 升級應用

    • 驗證權限

      1. 修改群組訪問權限

        1. 設置
        2. 用戶&公司
        3. 群組
        4. 點擊 example.example_category / example.example_groups_a
          1. 編輯
          2. 訪問權限
          3. 添加明細行
            • 名稱隨意
            • 對象, 點擊搜索更多后搜索並選擇example.example
            • 讀, 寫, 創建, 刪除權限都勾上
          4. 添加明細行
            • 名稱隨意
            • 對象, 點擊搜索更多后搜索並選擇example.example1
            • 讀, 寫, 創建, 刪除權限都勾上
          5. 保存
      2. 新建用戶

        1. 設置
        2. 用戶&公司
        3. 用戶
        4. 創建
        5. example.example_category 選擇 example.example_groups_a, 其他隨意
        6. 保存
        7. 點擊中間的動作下拉框
          • 更改密碼
            • 輸入密碼
            • 更改密碼
      3. 登錄剛才新建的用戶

        • 點擊創建
        • 是不是發現select沒有了, 哈哈哈哈哈哈
  10. 給name整個sequence

    • 新建文件夾addons/example/data
    • 新建文件addons/example/data/example_sequence.xml
    修改addons/example/data/example_sequence.xml
    <?xml version="1.0" encoding="utf-8"?>
    <odoo>
        <!--
        年代: %(year)s
        年份: %(y)s
        月: %(month)s
        日: %(day)s
        某年某日: %(doy)s
        某年某周: %(woy)s
        某周某天 (0:周一): %(weekday)s
        時 00->24: %(h24)s
        時 00->12: %(h12)s
        分: %(min)s
        秒: %(sec)s
        -->
        <record id="example_sequence" model="ir.sequence">
            <field name="name">example.example</field>
            <field name="code">example.example</field>
            <field name="prefix">OUT%(year)s%(month)s%(day)s</field>
    
            <!--序列大小-->
            <field name="padding">4</field>
            <field name="company_id" eval="False"/>
        </record>
    </odoo>
    
    • 將文件路徑添加到__manifest__.py中
    'data': [
        'data/example_sequence.xml',
    ...
    
    • 修改addons/example/models/models.py

      ...
      name = fields.Char(default=lambda self: self.env['ir.sequence'].next_by_code(self._name))
      ...
      
    • 重復步驟4

    • 此時會發現新建數據時. 會自動生成name

  11. 繼承mail模塊

    • 修改__manifest__.py

      ...
      'depends': ['base', 'mail'],
      ...
      
    • 重啟odoo后, 查看example應用信息的技術數據, 可以發現依賴多了個mail

    修改addons/example/models/models.py
    ...
    _inherit = ['mail.thread']
    my_datetime = fields.Datetime(default=fields.Datetime.now, track_visibility='onchange')
    # my_date = fields.Date(default=fields.Date.today)
    
    my_date = fields.Date(default=lambda self: datetime.date.today() + datetime.timedelta(days=4),
                        track_visibility='always'
                        )
    def button4(self):
        # 通過用戶test的id, 實現點擊關注用戶test,
        # 獲取id方法設置->用戶&公司->test->相關的業務伙伴->test 查看當前URL id
        self.message_subscribe(partner_ids=[7])
    
    def button5(self):
        # 點擊添加備注
        self.message_post(body='emmm')
    ...
    
    修改addons/example/views/views.xml文件
    ...
    <field name="arch" type="xml">
        <form>
    
            <div class="oe_chatter">
                <field name="message_follower_ids" widget="mail_followers"/>
                <field name="message_ids" widget="mail_thread"/>
            </div>
    
            <header>
    ...
        <button states="3" name="button1" string="當前狀態值為3, 點擊設置為1" class="oe_highlight" type="object"/>
    
        <button name="button4" string="老鐵, 點個關注" class="oe_highlight" type="object"/>
        <button name="button5" string="emmm" class="oe_highlight" type="object"/>
    
        <field name="state" widget="statusbar" statusbar_visible="1,2,3"/>
    ...
    
    • track_visibility

      記錄到備注中

      • track_visibility='onchange' :修改該字段時記錄
      • track_visibility='always': 編輯track_visibility為onchange或always對應的字段時記錄
    • 重復步驟4
      • 此時會發現多了塊區域顯示備注啥的
  12. 重載系統方法

    修改addons/example/models/models.py
    ...
    @api.onchange('active')
    def onchange_active(self):
        # 修改記錄的active字段時, 設置note內容
        self.update(dict(note='你敢改我active, 我就敢改我自己!'))
    
    def unlink(self):
        # 重寫系統刪除記錄函數
        for order in self:
            if len(order.name) > 10:
                raise UserError('這誰起的名字這么長, 俺不願意刪, 改短點!!!')
        return super().unlink()
    
    @api.model
    def create(self, vals):
        # 新建記錄后, 點擊保存后執行
        # 很奇怪的地方就是float, int類型設定的默認值, 沒有獲取到
        return super().create(vals)
    
    def write(self, vals):
        # 修改記錄后, 點擊保存后執行, vals包括被修改后的值
        # 修改name后: vals = {'name': 'OUT201908sa300012asooss'}
        return super().write(vals)
    ...
    
    • onchange: 修改指定字段時執行
    • unlink: 刪除數據(記錄)時執行
    • create: 創建數據(記錄)時執行
    • write: 修改數據(記錄)執行
    • 重復步驟4
  13. 計算字段

    會自動計算的字段

    修改addons/example/models/models.py
    ...
    name_and_price = fields.Char(
        compute='_compute_price_add_state_value'
    )
        
    @api.depends('price', 'name')
    def _compute_price_add_state_value(self):
        # 添加計算字段
        for order in self:
            order.name_and_price = '{} {}'.format(order.name, order.price)
    
    @api.onchange('price', 'name')
    def change_price_add_state_value(self):
        # 修改時,自動更新計算字段
        self.update(dict(
            name_and_price='{} {}'.format(self.name, self.price)
        ))
    ...
    
    修改addons/example/views/views.xml文件
    ...
    <record model="ir.ui.view" id="example.list">
        <field name="name">列表顯示字段</field>
        <field name="model">example.example</field>
        <field name="arch" type="xml">
        <tree>
            <field name="name_and_price"/>
    ...
                    <field name="select" required="0"/>
                    <field name="name_and_price"/>
                </group>
    ...
    

    重復步驟4

  14. 關系字段

    將當前字段綁定到某個字段, 值隨之變化, 可選擇存或不存到數據庫

    修改addons/example/models/models.py
    ...
    # store: 是否把關聯字段存到數據庫
    my_related1 = fields.Integer(related='em1_mo.fuck_id', string='關聯到em1_mo.fuck_id', store=True)
    my_related2 = fields.Date(related='my_date', string='關聯到my_date', store=True)
    ...
    
    修改addons/example/views/views.xml文件
    ...
    <record model="ir.ui.view" id="example.list">
        <field name="name">列表顯示字段</field>
        <field name="model">example.example</field>
        <field name="arch" type="xml">
        <tree>
            <field name="my_related1"/>
            <field name="my_related2"/>
    ...
                    <field name="name_and_price"/>
                    <field name="my_related1"/>
                    <field name="my_related2"/>
                </group>
    ...
    

    重復步驟4

  15. 查看模型, 菜單等

    • 點擊設置
    • 點擊技術
  16. 項目地址:https://github.com/dhgdhg/odoo-tutorial

  17. controller 回頭再更


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM