<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 指導規范 [TOC] ## 模塊構造 ### 文件夾 模塊的文件夾列表及對應作用: * `data/` 演示和實際數據的xml * `models/` 模型定義 * `controllers/` 包含控制器 * `views/` 包含視圖和模板 * `static/` 包含頁面相關的,一般劃分為`css/`,`/js`,`/img`... 其他可選的文件夾: * `wizard/` 放臨時的model和視圖 * `report/` 存放報表相關的python對象和xml * `tests/` 存放python和yml測試用例 ### 文件命名 一般把后臺視圖和前端頁面視圖分兩個文件夾存放。一系列業務model放置在一個文件里,如果只有一個model它的名字就與模型名一致。如: * `models/<main_model>.py` * `models/<inherited_main_model>.py` * `views/<main_model>_templates.xml` * `views/<main_model>_views.xml` 數據文件根據其內容分開命名,如果demo和data,文件名是以主模型名命令,并以_demo.xml,_data.xml結尾 控制器中唯一的文件被命名為`main.py`。如果需要從其他的模塊中繼承控制器,就命令為`<module_name>.py` 由于靜態文件在前端頁面和后臺頁面是共用的,css、js、xml文件一般以該組的名字來結尾如:`im_chat_common.css, im_chat_common.js`,如果模塊只有唯一一個文件,就直接用`module_name.ext` 對于wizards,一般命名為`<main_transient>.py,<main_transient>_views.xml` 對于報表,一般命名為`<report_name_A>_report.py,<report_name_A>_report_views.py` 對于打印報表,一般命名為`<print_report_name>_reports.py , <print_report_name>_templates.xml` 完整的文件列表如下: ~~~ addons/<my_module_name>/ |-- __init__.py |-- __manifest__.py |-- controllers/ | |-- __init__.py | |-- <inherited_module_name>.py | `-- main.py |-- data/ | |-- <main_model>_data.xml | `-- <inherited_main_model>_demo.xml |-- models/ | |-- __init__.py | |-- <main_model>.py | `-- <inherited_main_model>.py |-- report/ | |-- __init__.py | |-- <main_stat_report_model>.py | |-- <main_stat_report_model>_views.xml | |-- <main_print_report>_reports.xml | `-- <main_print_report>_templates.xml |-- security/ | |-- ir.model.access.csv | `-- <main_model>_security.xml |-- static/ | |-- img/ | | |-- my_little_kitten.png | | `-- troll.jpg | |-- lib/ | | `-- external_lib/ | `-- src/ | |-- js/ | | `-- <my_module_name>.js | |-- css/ | | `-- <my_module_name>.css | |-- less/ | | `-- <my_module_name>.less | `-- xml/ | `-- <my_module_name>.xml |-- views/ | |-- <main_model>_templates.xml | |-- <main_model>_views.xml | |-- <inherited_main_model>_templates.xml | `-- <inherited_main_model>_views.xml `-- wizard/ |-- <main_transient_A>.py |-- <main_transient_A>_views.xml |-- <main_transient_B>.py `-- <main_transient_B>_views.xml ~~~ ## XML文件 ### 格式 當定義一個記錄的xml時,需要一個`<record>`標記: * 將id屬性放在model前面 * 對于字段的字義,name屬性放在最前面,然后放`value`,`eval`,之后再按重要程度放其他屬性如widget, options * 根據model將record進行分組,但在action/menu/views有依賴關系時該規則不好用 * 使用好的命名習慣 * `<data>`標簽只在設置不可更新的數據時用,使用`noupdate=1` ~~~ <record id="view_id" model="ir.ui.view"> <field name="name">view.name</field> <field name="model">object_name</field> <field name="priority" eval="16"/> <field name="arch" type="xml"> <tree> <field name="my_field_1"/> <field name="my_field_2" string="My Label" widget="statusbar" statusbar_visible="draft,sent,progress,done" /> </tree> </field> </record> ~~~ odoo還支持一些自定義的標簽如: * `menuitem` - 用于定義一個`ir.ui.menu` * `workflow` - `<workflow>`標簽向工作流發送信號 * `template` - 用于定義一個只需要`arch`片段的view * `report` - 用于定義報表action * `act_window`- 當record用不了的時候用它 ### 命名xml_id #### 安全、視圖和action * 菜單 - `<model_name>_menu` * 視圖 - `<model_name>_view_<view_type>`, view_type=>`kanban, form, tree, search...` * action - 主action命名為`<model_name>_action`,其他用`_<detail>`后綴,detail可用于簡要描述該action功能 * 分組 - `<model_name>_group_<group_name>` ,`group_name`是分組名如用戶、管理員…… * 規則 - `<model_name>_rule_<concerned_group>`,concerned_group 代表對應組的簡寫如user,public... ~~~ <!-- views and menus --> <record id="model_name_view_form" model="ir.ui.view"> ... </record> <record id="model_name_view_kanban" model="ir.ui.view"> ... </record> <menuitem id="model_name_menu_root" name="Main Menu" sequence="5" /> <menuitem id="model_name_menu_action" name="Sub Menu 1" parent="module_name.module_name_menu_root" action="model_name_action" sequence="10" /> <!-- actions --> <record id="model_name_action" model="ir.actions.act_window"> ... </record> <record id="model_name_action_child_list" model="ir.actions.act_window"> ... </record> <!-- security --> <record id="module_name_group_user" model="res.groups"> ... </record> <record id="model_name_rule_public" model="ir.rule"> ... </record> <record id="model_name_rule_company" model="ir.rule"> ... </record> ~~~ #### 繼承xml 繼承視圖的命名方式是`<base_view>_inherit_<current_module_name>` ~~~ <record id="inherited_model_view_form_inherit_my_module" model="ir.ui.view"> ... </record> ~~~ ## python ### PEP8 使用linter來幫助顯示語法錯誤和警告,odoo遵循了大部分的python標準,但忽略了以下幾項: * E501: line too long * E301: expected 1 blank line, found 0 * E302: expected 2 blank lines, found 1 * E126: continuation line over-indented for hanging indent * E123: closing bracket does not match indentation of opening bracket's line * E127: continuation line over-indented for visual indent * E128: continuation line under-indented for visual indent * E265: block comment should start with '# ' ### 導入 導入的順序為: 1. 導入標準庫,每個庫一行 2. 導入odoo 3. 從odoo模塊導入(需要的時候) ~~~ # 1 : imports of python lib import base64 import re import time from datetime import datetime # 2 : imports of odoo import odoo from odoo import api, fields, models # alphabetically ordered from odoo.tools.safe_eval import safe_eval as eval from odoo.tools.translate import _ # 3 : imports from odoo modules from odoo.addons.website.models.website import slug from odoo.addons.web.controllers.main import login_redirect ~~~ ### 符合習慣的python編程 * 每個文件放置`# -*- coding: utf-8 -*-`在頭一行 * 從更易讀的角度出發 * 不用clone() ~~~ # bad new_dict = my_dict.clone() new_list = old_list.clone() # good new_dict = dict(my_dict) new_list = list(old_list) ~~~ * Python字典:創建和修改 ~~~ # -- creation empty dict my_dict = {} my_dict2 = dict() # -- creation with values # bad my_dict = {} my_dict['foo'] = 3 my_dict['bar'] = 4 # good my_dict = {'foo': 3, 'bar': 4} # -- update dict # bad my_dict['foo'] = 3 my_dict['bar'] = 4 my_dict['baz'] = 5 # good my_dict.update(foo=3, bar=4, baz=5) my_dict = dict(my_dict, **my_dict2) ~~~ * 使用有意義的變量名、函數名、類名 * 臨時變量有時可以讓賦值給對象更簡單: ~~~ # pointless schema = kw['schema'] params = {'schema': schema} # simpler params = {'schema': kw['schema']} ~~~ * 為了簡單可使用多重返回 ~~~ # a bit complex and with a redundant temp variable def axes(self, axis): axes = [] if type(axis) == type([]): axes.extend(axis) else: axes.append(axis) return axes # clearer def axes(self, axis): if type(axis) == type([]): return list(axis) # clone the axis else: return [axis] # single-element list ~~~ * 內置函數 * 正確使用list,dict,map * 集合也可以當成布爾型 ~~~ bool([]) is False bool([1]) is True bool([False]) is True ~~~ * 循環 ~~~ # creates a temporary list and looks bar for key in my_dict.keys(): "do something..." # better for key in my_dict: "do something..." # creates a temporary list for key, value in my_dict.items(): "do something..." # only iterates for key, value in my_dict.iteritems(): "do something..." ~~~ * 使用dict.setdefault ~~~ # longer.. harder to read values = {} for element in iterable: if element not in values: values[element] = [] values[element].append(other_value) # better.. use dict.setdefault method values = {} for element in iterable: values.setdefault(element, []).append(other_value) ~~~ * 添加注釋 ### odoo中編程 * 避免自定義生成器和裝飾器,只使用odoo API已有的 * 使用更易理解的方法名 #### 讓你的方法可以批量處理 當添加一個函數時,確保它可以處理多重數據,如通過`api.multi()`裝飾器,可以在self上進行循環處理 ~~~ @api.multi def my_method(self) for record in self: record.do_cool_stuff() ~~~ 避免使用`api.one`裝飾器,因為它可能不會像你想象中一樣工作。 為了更好的性能,比如當定義一個狀態按鈕時,不在api.multi循環里用search和search_count方法,而用read_group一次計算 ~~~ @api.multi def _compute_equipment_count(self): """ Count the number of equipement per category """ equipment_data = self.env['hr.equipment'].read_group([('category_id', 'in', self.ids)], ['category_id'], ['category_id']) mapped_data = dict([(m['category_id'][0], m['category_id_count']) for m in equipment_data]) for category in self: category.equipment_count = mapped_data.get(category.id, 0) ~~~ #### 擴散上下文環境 在新API中,context變量是不能修改的。可以通過`with_context`來使用新的運行環境調用方法。 ~~~ records.with_context(new_context).do_stuff() # all the context is replaced records.with_context(**additionnal_context).do_other_stuff() # additionnal_context values override native context ones ~~~ 如果需要創建一個新的context來對某對象進行操作, 選擇一個好名字,以模塊名為前綴用隔離它的影響,如:`mail_create_nosubscribe, mail_notrack, mail_notify_user_signature...` #### 盡量使用ORM 當ORM可以實現的時候盡量使用ORM而不要直接寫sql,因為它可能會繞過orm的一些規則如權限、事務等 ,還會讓代碼變得難讀且不安全。 ~~~ # very very wrong self.env.cr.execute('SELECT id FROM auction_lots WHERE auction_id in (' + ','.join(map(str, ids))+') AND state=%s AND obj_price > 0', ('draft',)) auction_lots_ids = [x[0] for x in self.env.cr.fetchall()] # no injection, but still wrong self.env.cr.execute('SELECT id FROM auction_lots WHERE auction_id in %s '\ 'AND state=%s AND obj_price > 0', (tuple(ids), 'draft',)) auction_lots_ids = [x[0] for x in self.env.cr.fetchall()] # better auction_lots_ids = self.search([('auction_id','in',ids), ('state','=','draft'), ('obj_price','>',0)]) ~~~ #### 不要進行sql注入 不要用python的+號連接符、%解釋符來拼sql ~~~ self.env.cr.execute('SELECT distinct child_id FROM account_account_consol_rel ' + 'WHERE parent_id IN ('+','.join(map(str, ids))+')') # better self.env.cr.execute('SELECT DISTINCT child_id '\ 'FROM account_account_consol_rel '\ 'WHERE parent_id IN %s', (tuple(ids),)) ~~~ #### 盡量把方法寫的短而簡單 #### 不要手動提交事務 odoo有自己的一套機制用于事務處理 #### 正確的使用翻譯方法 odoo用一個下劃線方法來表明某字段需要翻譯,該方法通過`from odoo.tools.translate import _`導入 一般情況下該方法只能被用在手動寫在代碼里的字符串的翻譯,不能用在動態的值中,翻譯方法的調用只能是`_('literal string')`,里面不能加其他的。 ~~~ # good: plain strings error = _('This record is locked!') # good: strings with formatting patterns included error = _('Record %s cannot be modified!') % record # ok too: multi-line literal strings error = _("""This is a bad multiline example about record %s!""") % record error = _('Record %s cannot be modified' \ 'after being validated!') % record # bad: tries to translate after string formatting # (pay attention to brackets!) # This does NOT work and messes up the translations! error = _('Record %s cannot be modified!' % record) # bad: dynamic string, string concatenation, etc are forbidden! # This does NOT work and messes up the translations! error = _("'" + que_rec['question'] + "' \n") # bad: field values are automatically translated by the framework # This is useless and will not work the way you think: error = _("Product %s is out of stock!") % _(product.name) # and the following will of course not work as already explained: error = _("Product %s is out of stock!" % product.name) # bad: field values are automatically translated by the framework # This is useless and will not work the way you think: error = _("Product %s is not available!") % _(product.name) # and the following will of course not work as already explained: error = _("Product %s is not available!" % product.name) # Instead you can do the following and everything will be translated, # including the product name if its field definition has the # translate flag properly set: error = _("Product %s is not available!") % product.name ~~~ 格式化字符串如`%s, %d`需要被保留,并以更明確的方式使用: ~~~ # Bad: makes the translations hard to work with error = "'" + question + _("' \nPlease enter an integer value ") # Better (pay attention to position of the brackets too!) error = _("Answer to question %s is not valid.\n" \ "Please enter an integer value.") % question ~~~ ### 符號和習慣 * 模型名-使用`.`分隔,模塊名做前綴 * 定義odoo模型時,使用單數形式的名字如res.user,res.partner * 定義wizard時,使用`<related_base_model>.<action>`,related_base_model 代表其所相關的模型名,action是其功能簡稱,如`account.invoice.make` * 定義報表模型時,使用`<related_base_model>.report.<action>`,和wizard一樣 * python類-使用駝峰命名方式 ~~~ class AccountInvoice(models.Model): ... class account_invoice(osv.osv): ... ~~~ * 變量名 * 模型變量使用駝峰命名方式 * 普通變量用下劃線+小寫字母 * 由于新api中記錄是集合形式,當變量不包含id時不以id作后綴 ~~~ ResPartner = self.env['res.partner'] partners = ResPartner.browse(ids) partner_id = partners[0].id ~~~ `One2Many, Many2Many`字段一般以ids作為后綴如:sale_order_line_ids `Many2One` 一般以_id為后綴如:partner_id, user_id * 方法習慣 * 計算字段- 計算方法一般是`_compute_<field_name>` * 搜索方法- `_search_<field_name>` * 默認方法- `_default_<field_name>` * onchange方法- `_onchange_<field_name>` * 約束方法 - `_check_<constraint_name>` * action方法- 一個對象的動作方法一般以action_開頭,它的裝飾器是`@api.multi`,如果它只使用單條計算,可在方法頭添加`self.ensure_one()` * 模型中屬性的順序 * 私有屬性:_name, _description, _inherit, ... * 默認方法和_default_get * 字段聲明 * 計算和搜索方法和字段聲明順序一致 * 約束方法(`@api.constrains`)和onchange方法(`@api.onchange`) * CRUD方法 * action方法 * 其他業務方法 ~~~ class Event(models.Model): # Private attributes _name = 'event.event' _description = 'Event' # Default methods def _default_name(self): ... # Fields declaration name = fields.Char(string='Name', default=_default_name) seats_reserved = fields.Integer(oldname='register_current', string='Reserved Seats', store=True, readonly=True, compute='_compute_seats') seats_available = fields.Integer(oldname='register_avail', string='Available Seats', store=True, readonly=True, compute='_compute_seats') price = fields.Integer(string='Price') # compute and search fields, in the same order of fields declaration @api.multi @api.depends('seats_max', 'registration_ids.state', 'registration_ids.nb_register') def _compute_seats(self): ... # Constraints and onchanges @api.constrains('seats_max', 'seats_available') def _check_seats_limit(self): ... @api.onchange('date_begin') def _onchange_date_begin(self): ... # CRUD methods (and name_get, name_search, ...) overrides def create(self, values): ... # Action methods @api.multi def action_validate(self): self.ensure_one() ... # Business methods def mail_user_confirm(self): ... ~~~ ## Javascript和CSS javascript: * 在所有javascript文件中使用`use strict;` * 使用linter * 不添加壓縮javascript庫 * 類名使用駝峰命名 * 如果javascript代碼需要全局運行,在website模塊中聲明一個if_dom_contains 方法 ~~~ odoo.website.if_dom_contains('.jquery_class_selector', function () { /*your code here*/ }); ~~~ CSS: * 將所有的class命名為`o_<module_name>`,如`o_forum` * 避免使用id * 使用bootstrap的class * 用下劃線+小寫來命名 ## GIT 建議看專門的GIT教程 # 更新API [http://www.odoo.com/documentation/10.0/reference/upgrade_api.html](http://www.odoo.com/documentation/10.0/reference/upgrade_api.html) * * * 譯自odoo官方文檔:[http://www.odoo.com/documentation/10.0/reference/guidelines.html](http://www.odoo.com/documentation/10.0/reference/guidelines.html) ,不當之處歡迎批評指正。 *內容發布自[http://www.jianshu.com/u/6fdae8ec06bc](http://www.jianshu.com/u/6fdae8ec06bc),轉載請注明出處* 作者:湖南玖零網絡科技 鏈接:http://www.jianshu.com/p/dfe15ef18769 來源:簡書 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看