Toggle navigation

Соглашение об оформлении кода в Odoo - Odoo 8.0

На этой странице представлены новые правила написания кода в 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

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>

Унаследованный 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: комментарий блока должен начинаться с '#'

Импорт

Порядок импорта библиотек определен следующим образом:

  1. Внешние библиотеки (по одной в строке, отсортированные и разделенные в python stdlib)

  2. Импорт openerp библиотек

  3. Импорт из модулей 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().

  • В атрибутах модели данных порядок должен быть следующим
    1. Частные атрибуты (_name, _description, _inherit, ...)

    2. Метод по умолчанию и _default_get

    3. Объявления полей

    4. Методы поиска и вычисления. Они должны идти в том же порядке, что и объявление поля

    5. Методы ограничений (@api.constrains) и onchange методы (@api.onchange)

    6. Методы CRUD (переопределения ORM)

    7. Методы действий

    8. И, наконец, другие методы, описывающие бизнес-логику.

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 ...