На этой странице представлены новые правила написания кода в Odoo. Эти рекомендации направлены на улучшение качества кода (улучшение удобочитаемости исходного кода ...) и приложений Odoo. Как правило, правильный код должен облегчить обслуживание, отладку, снизить сложность и повысить надежность.
Структура модуля
Каталоги
Модуль организован в виде нескольких каталогов:
data/ : демо-данные и файлы данных xml
models/ : здесь определяются модели данных
controllers/ : содержит контроллеры (http маршруты).
views/ : содержит представления и шаблоны
static/ : содержит веб компоненты, подразделяется на подкаталоги по содержимому css/, js/, img/, lib/, ...
Наименование файлов
Для объявления представлений для бэкэнда и фронтэнда, разделите их на два разных файла
Для моделей данных, разделите бизнес-логику на несколько наборов моделей данных, в каждом наборе выберите основную модель, она даст свое имя набору. Если есть только одна модель данных, то ее имя будет являться именем модуля. Для каждого набора с именем <main_model> могут быть созданы следующие файлы:
models/<main_model>.py
models/<inherited_main_model>.py
views/<main_model>_templates.xml
views/<main_model>_views.xml
Например, модуль sale объявляет модели данных sale_order
и sale_order_line
, где доминирует sale_order
. Таким образом, файлы <main_model>
будут называться: models/sale_order.py
и views/sale_order_views.py
.
Для data разделите их по видам: демо или служебные данные. Имя файла будет именем main_model, с суффиксом _demo.xml или _data.xml.
Для controller нужно создать единственный файл с именем main.py.
Для статичных файлов, шаблоны имен будут <module_name>.ext (то есть.: static/js/im_chat.js, static/css/im_chat.css, static/xml/im_chat.xml, ...). Не ссылайтесь на данные (изображения, библиотеки) за пределами Odoo: не используйте ссылки на изображения - копируйте изображения в соотвествующий каталог.
Полное дерево каталогов должно выглядеть следующим образом:
addons/<my_module_name>/
|-- __init__.py
|-- __openerp__.py
|-- controllers/
| |-- __init__.py
| `-- main.py
|-- data/
| |-- <main_model>_data.xml
| `-- <inherited_main_model>_demo.xml
|-- models/
| |-- __init__.py
| |-- <main_model>.py
| `-- <inherited_main_model>.py
|-- 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
Примечание
Имена файлов должны содержать только [a-z0-9_]
(строчные буквы, цифры и _
)
Предупреждение
Используйте правильные разрешения для файлов: для каталогов - 755, файлов - 644.
XML файлы
Формат
При объявлении записи в XML,
Поставьте атрибут
id
перед атрибутомmodel
При объявления поля первым ставиться атрибут
name
. Затем поместите значение поля в тегfield
затем атрибутeval
и, наконец, другие атрибуты (widget, options, ...), следующие в порядке важности.Попробуйте сгруппировать записи по моделям данных. В случае зависимостей между действием/меню/представлениями соглашение может быть неприменимым.
Использовать соглашение об создании имен, определенное далее
Тег [UNKNOWN NODE problematic]<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>
Формирование имен xml_id
Безопасность, Представление и Действие
Используйте следующий шаблон:
для меню:
<model_name>_menu
Для представления:
<model_name>_view_<view_type>
, где view_type может бытьkanban
,form
,tree
,search
, ...Для действия: основное действие соответствует
<model_name>_action
. Остальные имеют суффикс_<detail>
, где detail запись прописными буквами поясняющая действие. Суффиксы используются только для тех моделей данных, где есть объявление нескольких действий.Для группы пользователей:
<model_name>_group_<group_name>
где group_name является именем группы, как правило 'user', 'manager', ...Для правила безопасности:
<model_name>_rule_<concerned_group>
где concerned_group короткое имя соответствующей группы ('user' для 'model_name_group_user', 'public' для неавторизованных пользователей, 'company' для систем с множеством компаний, ...).
<!-- views and menus -->
<record id="model_name_menu" model="ir.ui.menu">
...
</record>
<record id="model_name_view_form" model="ir.ui.view">
...
</record>
<record id="model_name_view_kanban" model="ir.ui.view">
...
</record>
<!-- 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="model_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>
Примечание
Имена представлений используют точечную нотацию my.model.view_type
или my.model.view_type.inherit
вместо * "This is the form view of My Model" [UNKNOWN NODE problematic].
Унаследованный XML-файл
Шаблон для формирования имени расширенного представления <base_view>_inherit_<current_module_name>
.Модуль может быть создан исключительно для расширения представления. Создайте суффикс для оригинального имени _inherit_<current_module_name>
где current_module_name техническое имя модуля расширяющего представление.
<record id="inherited_model_view_form_inherit_my_module" model="ir.ui.view">
...
</record>
Python
Параметры PEP8
Использование linter может помочь в подсветке синтаксиса, семантических предупреждений или ошибки. Исходный код Odoo пытается уважать стандарты Python, но некоторые из них можно игнорировать.
E501: слишком длинная строка
E301: ожидается 1 пустая строка, найдено 0
E302: ожидается 2 пустые строки, найдено 1
E126: строка продолжается надписью для подвешивания отступа
E123: закрывающая скобка не соответствует отступу строки открывающей скобки
E127: строка заходит за отступ для визуализации отступа
E128: строка продолжения под отступом для визуального отступа
E265: комментарий блока должен начинаться с '#'
Импорт
Порядок импорта библиотек определен следующим образом:
Внешние библиотеки (по одной в строке, отсортированные и разделенные в python stdlib)
Импорт
openerp
библиотекИмпорт из модулей Odoo (редко, и только при необходимости)
Внутри этих 3-х групп строки сортируются по алфавиту.
# 1 : imports of python lib
import base64
import re
import time
# 2 : imports of openerp
import openerp
from openerp import api, fields, models # alphabetically ordered
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
# 3 : imports from odoo modules
from openerp.addons.website.models.website import slug
from openerp.addons.web.controllers.main import login_redirect
Идиомы
Лучше
%
чем [UNKNOWN NODE problematic].format () [UNKNOWN NODE problematic], лучше [UNKNOWN NODE problematic]% (varname) `` чем position (Это лучше для перевода)Старайтесь избегать генераторов и декораторов
Всегда предпочитайте читабельность над лаконичностью или используйте осмысленные названия функций или идиом.
Используйте понимание списков, словарей и базовых манипуляции с помощью
map
,filter
,sum
, ... Это делает код более читаемым.То же касается методов, наборов записей: используйте
filtered
,mapped
,sorted
, ...Файл python должен начинаться со строки
# - * - coding: utf-8 - * -
Используйте
UserError
, определенный вopenerp.exceptions
вместо переопределенияWarning
, или найдите более подходящее исключение в exceptions.pyДокументируйте свой код (docstring для методов, простые комментарии для сложной части кода)
Используйте осмысленные имена переменных/классов/методов
Символы
Определение класса Odoo Python: используйте 'camelcase <https://ru.wikipedia.org/wiki/CamelCase>' при написании кода в api v8, подчеркивание и нижний регистр для старой api.
class AccountInvoice(models.Model):
...
class account_invoice(osv.osv):
...
- Имя переменной:
Используйте camelcase при определении переменных в модели данных
Используйте нижний регистр и подчеркивание при определении обычных переменных.
Так как новый API работает с записью или набором записей вместо списка идентификаторов, не нужно добавлять имя переменной с _id или _ids, если они не содержат 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 : шаблон метода расчета _compute_<имя_поля>
Search method: шаблон метода поиска _search_<field_name>
Default method: шаблон метода по умолчанию _default_<field_name>
Onchange method: шаблон метода onchange _onchange_<field_name>
Constraint method : шаблон метода ограничения _check_<constraint_name>
Action method : начинается с префикса action_. Его декоратор
@api.multi
, но когда используется только одна запись добавьте в начале методаself.ensure_one()
.
- В атрибутах модели данных порядок должен быть следующим
Частные атрибуты (
_name
,_description
,_inherit
, ...)Метод по умолчанию и
_default_get
Объявления полей
Методы поиска и вычисления. Они должны идти в том же порядке, что и объявление поля
Методы ограничений (
@api.constrains
) и onchange методы (@api.onchange
)Методы CRUD (переопределения ORM)
Методы действий
И, наконец, другие методы, описывающие бизнес-логику.
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 that 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
def create(self):
...
# Action methods
@api.multi
def action_validate(self):
self.ensure_one()
...
# Business methods
def mail_user_confirm(self):
...
Javascript и CSS
Для javascript :
use strict;
рекомендуется во всех файлах javascriptИспользуйте linter (jshint, ...)
Никогда не добавляйте минифицированные библиотеки Javascript
Используйте camelcase для объявления класса
Для CSS :
Префикс всех ваших классов o_<module_name> где module_name - техническое имя модуля ('sale', 'im_chat', ...) или основной маршрут, зарезервированный модулем (главным образом для веб-сайта, т.е. : 'o_forum' для модуля website_forum). Единственным исключением для этого правила является веб-клиент: он просто использует префикс o_.
Избегайте использования id
Используйте классы Bootstrap'a
Используйте нижний регистр для обозначения класса
Git
Коммиты
Префикс для вашего коммита
[IMP] для улучшения (improvement)
[FIX] для исправления ошибок
[REF] для рефакторинга
[ADD] для добавления новых ресурсов
[REM] для удаления ресурсов
[MERGE] для merge коммитов (только для прямого/обратного портирования)
[CLA] для подписи индивидуальной авторской лицензии Odoo
Затем в теле сообщении укажите часть кода, на которую повлияли ваши изменения (имя модуля, библиотека, transversal объект, ...) и описание изменений.
Всегда делайте коммит информативным: он должно быть самодостаточным, включать имя модуля, который был изменен, и причину изменения. Не используйте одиночные слова, такие как «исправление» или «улучшения».
Избегайте коммитов, которые одновременно воздействуют на несколько модулей. Попытайтесь их разделить таким образом чтобы модули были независимы друг от друга (будет полезно, если нам нужно будет откатить модуль отдельно).
[FIX] website, website_mail: remove unused alert div, fixes look of input-group-btn
Bootstrap's CSS depends on the input-group-btn
element being the first/last child of its parent.
This was not the case because of the invisible
and useless alert.
[IMP] fields: reduce memory footprint of list/set field attributes
[REF] web: add module system to the web client
This commit introduces a new module system for the javascript code.
Instead of using global ...
Примечание
Используйте длинное описание, чтобы объяснить почему а не что, что и так можно увидеть в diff