ORM API¶
- Модуль Object Relational Mapping:
Ієрархічна структура
Узгодженість і перевірка обмежень
Метадані об’єкта залежать від його статусу
Оптимізована обробка за складним запитом (кілька дій одночасно)
Значення полів за замовчуванням
Оптимізація дозволів
Постійний об’єкт: DB postgresql
Перетворення даних
Багаторівнева система кешування
Два різних механізми успадкування
- Багатий набір типів полів:
classical (varchar, integer, boolean, …)
relational (one2many, many2one, many2many)
функціональний
Моделі¶
Поля моделі визначаються як атрибути самої моделі:
from odoo import models, fields
class AModel(models.Model):
_name = 'a.model.name'
field1 = fields.Char()
Попередження
це означає, що ви не можете визначити поле та метод з однаковою назвою, останній мовчки перезапише попередні.
За замовчуванням мітка поля (назва, видима для користувача) є версією назви поля з великої літери, це можна замінити параметром string
.
field2 = fields.Integer(string="Field Label")
Перелік типів полів і параметрів див. посилання на поля.
Значення за замовчуванням визначаються як параметри полів або як значення:
name = fields.Char(default="a value")
або як функція, викликана для обчислення значення за замовчуванням, яке має повернути це значення:
def _default_name(self):
return self.get_value()
name = fields.Char(default=lambda self: self._default_name())
API
- class odoo.models.BaseModel[source]¶
Базовий клас для моделей Odoo.
Моделі Odoo створюються шляхом успадкування одного з наступного:
Model
для звичайних моделей, що зберігаються в базі данихTransientModel
для тимчасових даних, які зберігаються в базі даних, але періодично автоматично видаляютьсяAbstractModel
для абстрактних суперкласів, призначених для спільного використання кількома моделями успадкування
Система автоматично створює кожну модель один раз для бази даних. Ці екземпляри представляють доступні моделі в кожній базі даних і залежать від того, які модулі встановлено в цій базі даних. Фактичний клас кожного екземпляра будується з класів Python, які створюють і успадковують відповідну модель.
Кожен екземпляр моделі є «recordset», тобто впорядкованою колекцією записів моделі. Набори записів повертаються такими методами, як
browse()
,search()
або доступ до полів. Записи не мають явного представлення: запис представлено як recordset з одного запису.Щоб створити клас, екземпляр якого не слід створювати, для атрибута
_register
можна встановити значення False.- _auto = False¶
Чи потрібно створити таблицю бази даних. Якщо встановлено значення
False
, замінітьinit()
, щоб створити таблицю бази даних.Автоматично встановлюється значення
True
дляModel
іTransientModel
,False
дляAbstractModel
.Порада
Щоб створити модель без жодної таблиці, успадкуйте від
AbstractModel
.
- _log_access¶
Чи повинен ORM автоматично генерувати та оновлювати Поля журналу доступу.
За замовчуванням використовується будь-яке значення, встановлене для
_auto
.
- _sql_constraints = []¶
SQL constraints [(name, sql_def, message)]
- _register = False¶
видимість реєстру
- _abstract = True¶
Чи є модель abstract.
Перегляньте також
- _transient = False¶
Чи є модель transient.
Перегляньте також
- _name = None¶
назва моделі (у крапковій нотації, простір імен модуля)
- _description = None¶
неформальна назва моделі
- _inherit = ()¶
Моделі, успадковані від Python:
- тип
str або list(str)
- _inherits = {}¶
словник {„parent_model“: „m2o_field“} зіставлення _name батьківських бізнес-об’єктів з назвами відповідних полів зовнішнього ключа для використання:
_inherits = { 'a.model': 'a_field_id', 'b.model': 'b_field_id' }
реалізує успадкування на основі композиції: нова модель відкриває всі поля успадкованих моделей, але не зберігає жодного з них: самі значення залишаються збереженими у зв’язаному записі.
Попередження
якщо в моделях
_inherits
-ed визначено декілька полів з однаковою назвою, успадковане поле відповідатиме останньому (у порядку списку успадкованих).
- _rec_name = None¶
поле для позначення записів, за замовчуванням:
name
- _order = 'id'¶
типове поле порядку для результатів пошуку
- _check_company_auto = False¶
Під час запису та створення викличте
_check_company
, щоб забезпечити узгодженість компаній у реляційних полях, які мають атрибутcheck_company=True
.
- _parent_name = 'parent_id'¶
поле many2one використовується як батьківське поле
- _parent_store = False¶
встановіть значення True, щоб обчислити поле parent_path.
Поряд із полем
parent_path
встановлює індексоване сховище деревоподібної структури записів, щоб забезпечити швидші ієрархічні запити до записів поточної моделі за допомогою доменівchild_of
іparent_of
оператори.
- _fold_name = 'fold'¶
поле для визначення згорнутих груп у представленнях канбан
AbstractModel¶
- odoo.models.AbstractModel[source]¶
alias of
odoo.models.BaseModel
Модель¶
- class odoo.models.Model(env, ids, prefetch_ids)[source]¶
Основний супер-клас для звичайних моделей Odoo, що зберігаються в базі даних.
Моделі Odoo створюються шляхом успадкування цього класу:
class user(Model): ...
Пізніше система створить екземпляр класу один раз для бази даних (у якій встановлено модуль класу).
- _auto = True¶
Чи потрібно створити таблицю бази даних. Якщо встановлено значення
False
, замінітьinit()
, щоб створити таблицю бази даних.Автоматично встановлюється значення
True
дляModel
іTransientModel
,False
дляAbstractModel
.Порада
Щоб створити модель без жодної таблиці, успадкуйте від
AbstractModel
.
- _abstract = False¶
Чи є модель abstract.
Перегляньте також
TransientModel¶
- class odoo.models.TransientModel(env, ids, prefetch_ids)[source]¶
Модель супер-класу для тимчасових записів, призначених для тимчасової стійкості та регулярної очистки.
TransientModel має спрощене керування правами доступу, усі користувачі можуть створювати нові записи та мати доступ лише до створених ними записів. Суперкористувач має необмежений доступ до всіх записів TransientModel.
- _transient_max_count = 0¶
maximum number of transient records, unlimited if
0
- _transient_max_hours = 1.0¶
maximum idle lifetime (in hours), unlimited if
0
- _transient_vacuum()[source]¶
Clean the transient records.
This unlinks old records from the transient model tables whenever the
_transient_max_count
or_transient_max_hours
conditions (if any) are reached.Actual cleaning will happen only once every 5 minutes. This means this method can be called frequently (e.g. whenever a new record is created).
Example with both max_hours and max_count active:
Suppose max_hours = 0.2 (aka 12 minutes), max_count = 20, there are 55 rows in the table, 10 created/changed in the last 5 minutes, an additional 12 created/changed between 5 and 10 minutes ago, the rest created/changed more than 12 minutes ago.
age based vacuum will leave the 22 rows created/changed in the last 12 minutes
count based vacuum will wipe out another 12 rows. Not just 2, otherwise each addition would immediately cause the maximum to be reached again.
the 10 rows that have been created/changed the last 5 minutes will NOT be deleted
Поля¶
- class odoo.fields.Field[source]¶
The field descriptor contains the field definition, and manages accesses and assignments of the corresponding field on records. The following attributes may be provided when instantiating a field:
- Параметри
string (str) – мітка поля, яке бачать користувачі; якщо не встановлено, ORM приймає назву поля в класі (з великої літери).
help (str) – підказка поля, яку бачать користувачі
readonly (bool) – чи є поле лише для читання (за замовчуванням:
False
). Це впливає лише на інтерфейс користувача. Будь-яке призначення полів у коді працюватиме (якщо поле є збереженим або інверсійним полем).required (bool) – чи обов’язкове значення поля (за замовчуванням:
False
)index (str) –
whether the field is indexed in database, and the kind of index. Note: this has no effect on non-stored and virtual fields. The possible values are:
"btree"
orTrue
: standard index, good for many2one"btree_not_null"
: BTREE index without NULL values (useful when mostvalues are NULL, or when NULL is never searched for)
"trigram"
: Generalized Inverted Index (GIN) with trigrams (good for full-text search)None
orFalse
: no index (default)
default (value or callable) – значення поля за умовчанням; це або статичне значення, або функція, яка приймає recordset і повертає значення; використовуйте
default=None
, щоб відхилити значення за замовчуванням для поляgroups (str) – список розділених комами xml-ідентифікаторів груп (string); це обмежує доступ до поля лише для користувачів вказаних груп
company_dependent (bool) – чи залежить значення поля від поточної компанії; Значення не зберігається в таблиці моделей. Він зареєстрований як
ir.property
. Коли потрібне значення поля company_dependent, виконується пошук уir.property
, пов’язаному з поточною компанією (і поточним записом, якщо така властивість існує). Якщо значення в записі змінено, це або змінює наявну властивість для поточного запису (якщо він існує), або створює нову для поточної компанії та res_id. Якщо значення змінено на стороні компанії, це вплине на всі записи, для яких значення не було змінено.copy (bool) – чи потрібно копіювати значення поля при дублюванні запису (за замовчуванням:
True
для звичайних полів,False
для полівone2many
та обчислюваних полів, включно з полями властивостей та пов’язаними з ними полями)store (bool) – чи зберігається поле в базі даних (за замовчуванням:
True
,False
для обчислюваних полів)group_operator (str) – агрегатна функція, що використовується
read_group()
при групуванні за цим полем. Підтримуються такі агрегатні функції: *array_agg
: значення, включаючи нулі, об’єднані у масив *count
: кількість рядків *count_distinct
: кількість різних рядків *bool_and
: true, якщо всі значення істинні, інакше false *bool_or
: true, якщо хоча б одне значення істинне, інакше false *max
: максимальне значення всіх значень *min
: мінімальне значення всіх значень *avg
: середнє арифметичне всіх значень *sum
: сума всіх значеньgroup_expand (str) – функція, що використовується для розширення результатів read_group при групуванні за поточним полем. .. code-block :: python @api.model def _read_group_selection_field(self, values, domain, order): return [„choice1“, „choice2“, …] # доступні варіанти вибору. @api.model def _read_group_many2one_field(self, records, domain, order): return records + self.search([custom_domain])
Обчислювані поля
- Параметри
compute (str) – назва методу, який обчислює поле .. див. також:: Додаткові поля/Обчислювальні поля
precompute (bool) –
whether the field should be computed before record insertion in database. Should be used to specify manually some fields as precompute=True when the field can be computed before record insertion. (e.g. avoid statistics fields based on search/read_group), many2one linking to the previous record, … (default:
False
)Попередження
Precomputation only happens when no explicit value and no default value is provided to create(). This means that a default value disables the precomputation, even if the field is specified as precompute=True.
Precomputing a field can be counterproductive if the records of the given model are not created in batch. Consider the situation were many records are created one by one. If the field is not precomputed, it will normally be computed in batch at the flush(), and the prefetching mechanism will help making the computation efficient. On the other hand, if the field is precomputed, the computation will be made one by one, and will therefore not be able to take advantage of the prefetching mechanism.
Following the remark above, precomputed fields can be interesting on the lines of a one2many, which are usually created in batch by the ORM itself, provided that they are created by writing on the record that contains them.
compute_sudo (bool) – чи потрібно переобчислювати поле від імені суперкористувача, щоб обійти права доступу (за замовчуванням
True
для збережених полів,False
для незбережених полів)recursive (bool) – whether the field has recursive dependencies (the field
X
has a dependency likeparent_id.X
); declaring a field recursive must be explicit to guarantee that recomputation is correctinverse (str) – назва методу, який інвертує поле (необов’язково)
search (str) – назва методу, який реалізує пошук по полю (необов’язково)
related (str) – послідовність назв полів
default_export_compatible (bool) –
whether the field must be exported by default in an import-compatible export
Перегляньте також
Основні поля¶
- class odoo.fields.Char[source]¶
Базове рядкове поле, може мати обмежену довжину, зазвичай відображається як однорядковий рядок у клієнтах.
- Параметри
size (int) – максимальний розмір значень, що зберігаються для цього поля
trim (bool) – вказує, чи буде значення обрізано чи ні (за замовчуванням
True
). Зауважте, що операція обрізання застосовується лише веб-клієнтом.translate (bool or callable) – увімкнути переклад значень поля; використовувати
translate=True
для перекладу значень поля в цілому;translate
також може бути викликом, так щоtranslate(callback, value)
перекладаєvalue
, використовуючиcallback(term)
для отримання перекладу термінів.
- class odoo.fields.Float[source]¶
Інкапсулює
float
.Точність цифр задається (необов’язковим) атрибутом
digits
.- Параметри
digits (tuple(int,int) or str) – пара (total, decimal) або рядок, що посилається на ім’я запису
DecimalPrecision
.
Коли плаваюча величина пов’язана з одиницею виміру, важливо використовувати правильний інструмент для порівняння або округлення значень з правильною точністю.
Клас Float надає для цього декілька статичних методів:
round()
округлити число з заданою точністю.is_zero()
перевірити, чи дорівнює число нулю з заданою точністю.compare()
порівняти два числа з заданою точністю.Приклад
To round a quantity with the precision of the unit of measure:
fields.Float.round(self.product_uom_qty, precision_rounding=self.product_uom_id.rounding)
To check if the quantity is zero with the precision of the unit of measure:
fields.Float.is_zero(self.product_uom_qty, precision_rounding=self.product_uom_id.rounding)
Щоб порівняти дві величини:
field.Float.compare(self.product_uom_qty, self.qty_done, precision_rounding=self.product_uom_id.rounding)
Помічник порівняння використовує семантику __cmp__ в історичних цілях, тому правильним, ідіоматичним способом використання цього помічника є такий:
якщо результат == 0, то перший та другий числа дорівнюють один одному якщо результат < 0, то перший дріб менший за другий якщо результат > 0, то перший дріб більший за другий
Розширені поля¶
- class odoo.fields.Binary[source]¶
Інкапсулює двійковий вміст (наприклад, файл).
- Параметри
attachment (bool) – чи слід зберігати поле як
ir_attachment
або у стовпчику таблиці моделі (за замовчуванням:True
).
- class odoo.fields.Html[source]¶
Інкапсулює вміст html-коду.
- Параметри
sanitize (bool) – чи потрібно очищати значення (за замовчуванням:
True
)sanitize_overridable (bool) – whether the sanitation can be bypassed by the users part of the
base.group_sanitize_override
group (default:False
)sanitize_tags (bool) – чи очищати теги (приймається лише білий список атрибутів, за замовчуванням:
True
)sanitize_attributes (bool) – чи очищати атрибути (приймається лише білий список атрибутів, за замовчуванням:
True
)sanitize_style (bool) – чи очищати атрибути стилів (за замовчуванням:
False
)strip_style (bool) – чи вилучати атрибути стилю (вилучаються і, відповідно, не очищуються, за замовчуванням:
False
)strip_classes (bool) – чи вилучати атрибути класів (за замовчуванням:
False
)
- class odoo.fields.Image[source]¶
Інкапсулює зображення, розширюючи
Binary
.Якщо розмір зображення перевищує обмеження
max_width
/max_height`
у пікселях, зображення буде змінено до граничного розміру зі збереженням співвідношення сторін.- Параметри
max_width (int) – максимальна ширина зображення (за замовчуванням:
0
, без обмежень)max_height (int) – максимальна висота зображення (за замовчуванням:
0
, без обмежень)verify_resolution (bool) – чи слід перевіряти роздільну здатність зображення, щоб переконатися, що вона не перевищує максимальну роздільну здатність зображення (за замовчуванням:
True
). Максимальну роздільну здатність зображення дивіться уodoo.tools.image.ImageProcess
(за замовчуванням:50e6
).
Примітка
Якщо
max_width
/max_height
не вказано (або встановлено у 0) іverify_resolution
має значення False, вміст поля не буде перевірено взагалі і слід використовувати полеBinary
.
- class odoo.fields.Monetary[source]¶
Інкапсулює
float
, виражений у заданомуres_currency
.Десяткова точність і символ валюти беруться з атрибута
currency_field
.
- class odoo.fields.Selection[source]¶
Інкапсулює ексклюзивний вибір між різними значеннями.
- Параметри
selection (list(tuple(str,str)) or callable or str) – визначає можливі значення для цього поля. Це може бути або список пар
(значення, мітка)
, або метод моделі, або ім’я методу.selection_add (list(tuple(str,str))) – надає розширення вибірки у випадку перевизначеного поля. Це список пар
(value, label)
або одиночних значень(value,)
, де одиночні значення повинні з’явитися у перевизначеному виділенні. Нові значення вставляються у порядку, який узгоджується з перевизначеним вибором і цим списком:: selection = [(„a“, „A“), („b“, „B“)] selection_add = [(„c“, „C“), („b“,)] > result = [(„a“, „A“), („c“, „C“), („b“, „B“)]ondelete –
забезпечує резервний механізм для будь-якого поля, перевизначеного за допомогою selection_add. Це дикт, який зіставляє кожну опцію з selection_add з резервною дією.
Ця запасна дія буде застосована до всіх записів, опція selection_add яких відповідає їй.
- Дії можуть бути будь-якими з наступних:
“set null“ – за замовчуванням для всіх записів з цим параметром буде встановлено значення False.
“cascade“ – усі записи з цією опцією буде видалено разом із самою опцією.
“set default“ – для всіх записів з цим параметром буде встановлено значення за замовчуванням визначення поля
’set VALUE“ – all records with this option will be set to the given value
<callable> – виклик, першим і єдиним аргументом якого буде набір записів, що містять вказаний параметр Selection, для кастомної обробки
Атрибут
selection
є обов’язковим, окрім випадківrelated
або розширених полів.
- class odoo.fields.Text[source]¶
Дуже схожий на
Char
, але використовується для довшого вмісту, не має розміру і зазвичай відображається як багаторядкове текстове поле.- Параметри
translate (bool or callable) – увімкнути переклад значень поля; використовувати
translate=True
для перекладу значень поля в цілому;translate
також може бути викликом, так щоtranslate(callback, value)
перекладаєvalue
, використовуючиcallback(term)
для отримання перекладу термінів.
Дата(час) поля¶
Dates
і Datetimes
є дуже важливими полями в будь-якій бізнес додаток. Їх неправильне використання може створити невидимі, але болючі помилки, цей розділ має на меті надати розробникам Odoo знання, необхідні для уникнення неправильного використання цих полів.
Під час призначення значення полю Date/Datetime дійсні такі параметри:
Об’єкт
date
абоdatetime
.Рядок у відповідному форматі сервера:
False
абоNone
.
Клас полів Date і Datetime має допоміжні методи для спроб перетворення на сумісний тип:
to_date()
перетвориться наdatetime.date
to_datetime()
перетвориться наdatetime.datetime
.
Example
Для аналізу date/datetimes, що надходять із зовнішніх джерел:
fields.Date.to_date(self._context.get('date_from'))
Рекомендації щодо порівняння Date / Datetime:
Поля дати можна лише порівнювати з об’єктами дати.
Поля дати й часу можна лише порівнювати з об’єктами дати й часу.
Попередження
Рядки, що представляють dates та datetimes, можна порівнювати між собою, однак результат може не бути очікуваним, оскільки рядок datetime завжди буде більшим за рядок date, тому така практика суворо не рекомендується.
Звичайні операції з dates та datetimes, такі як додавання, віднімання або вибірка початку/кінця періоду, доступні через Date
і Datetime
. Ці помічники також доступні шляхом імпорту odoo.tools.date_utils
.
Примітка
Часові зони
Поля дати й часу зберігаються в базі даних як стовпці timestamp without timezone
і зберігаються в часовому поясі UTC. Це задумом, оскільки це робить базу даних Odoo незалежною від часового поясу системи хостинг-сервера. Перетворенням часових поясів повністю керує клієнт.
- class odoo.fields.Date[source]¶
Інкапсулює об’єкт python
date
.- static start_of(value, granularity)[source]¶
Об’єкт дати/часу, що відповідає початку вказаного періоду.
- Параметри
value – початкова дата або час.
granularity – тип періоду в рядку, може бути рік, квартал, місяць, тиждень, день або година.
- Повертає
об’єкт дати/часу, що відповідає початку вказаного періоду.
- static end_of(value, granularity)[source]¶
Отримати кінець періоду часу за датою або часом.
- Параметри
value – початкова дата або час.
granularity – Тип періоду в рядку, може бути рік, квартал, місяць, тиждень, день або година.
- Повертає
Об’єкт дати/часу, що відповідає початку вказаного періоду.
- static add(value, *args, **kwargs)[source]¶
Повернути суму
value
іrelativedelta
.- Параметри
value – початкова дата або час.
args – позиційні аргументи передавати безпосередньо до
relativedelta
.kwargs – ключове слово args для передачі безпосередньо до
relativedelta
.
- Повертає
отриману дату/час.
- static subtract(value, *args, **kwargs)[source]¶
Повертає різницю між
value
таrelativedelta
.- Параметри
value – початкова дата або час.
args – позиційні аргументи передавати безпосередньо до
relativedelta
.kwargs – ключове слово args для передачі безпосередньо до
relativedelta
.
- Повертає
отриману дату/час.
- static today(*args)[source]¶
Поверніть поточний день у форматі, очікуваному ORM.
Примітка
Ця функція може бути використана для обчислення значень за замовчуванням.
- static context_today(record, timestamp=None)[source]¶
Повертає поточну дату у часовому поясі клієнта у форматі, придатному для полів дати.
Примітка
Цей метод можна використовувати для обчислення значень за замовчуванням.
- Параметри
record – набір записів, з якого буде отримано часовий пояс.
timestamp (datetime) – необов’язкове значення часу, яке буде використовуватися замість поточної дати та часу (це має бути час, звичайні дати не можуть конвертуватися між часовими поясами).
- Тип повернення
date
- class odoo.fields.Datetime[source]¶
Інкапсулює об’єкт python
datetime
.- static start_of(value, granularity)[source]¶
Об’єкт дати/часу, що відповідає початку вказаного періоду.
- Параметри
value – початкова дата або час.
granularity – тип періоду в рядку, може бути рік, квартал, місяць, тиждень, день або година.
- Повертає
об’єкт дати/часу, що відповідає початку вказаного періоду.
- static end_of(value, granularity)[source]¶
Отримати кінець періоду часу за датою або часом.
- Параметри
value – початкова дата або час.
granularity – Тип періоду в рядку, може бути рік, квартал, місяць, тиждень, день або година.
- Повертає
Об’єкт дати/часу, що відповідає початку вказаного періоду.
- static add(value, *args, **kwargs)[source]¶
Повернути суму
value
іrelativedelta
.- Параметри
value – початкова дата або час.
args – позиційні аргументи передавати безпосередньо до
relativedelta
.kwargs – ключове слово args для передачі безпосередньо до
relativedelta
.
- Повертає
отриману дату/час.
- static subtract(value, *args, **kwargs)[source]¶
Повертає різницю між
value
таrelativedelta
.- Параметри
value – початкова дата або час.
args – позиційні аргументи передавати безпосередньо до
relativedelta
.kwargs – ключове слово args для передачі безпосередньо до
relativedelta
.
- Повертає
отриману дату/час.
- static now(*args)[source]¶
Повертає поточний день і час у форматі, який очікує ORM.
Примітка
Ця функція може бути використана для обчислення значень за замовчуванням.
- static context_timestamp(record, timestamp)[source]¶
Повертає задану мітку часу, перетворену в часовий пояс клієнта.
Примітка
Цей метод не призначено для використання як ініціалізатор за замовчуванням, оскільки поля часу даних автоматично конвертуються при відображенні на стороні клієнта. Для значень за замовчуванням слід використовувати
now()
.- Параметри
record – набір записів, з якого буде отримано часовий пояс.
timestamp (datetime) – значення місцевого часу (виражене в UTC) для перетворення в часовий пояс клієнта.
- Повертає
мітку часу перетворено на час з урахуванням часового поясу у контекстному часовому поясі.
- Тип повернення
datetime
Реляційні поля¶
- class odoo.fields.Many2one[source]¶
Значенням такого поля є набір записів розміром 0 (жодного запису) або 1 (один запис).
- Параметри
comodel_name (str) – назва цільової моделі
Обов'язкова
, за винятком пов’язаних або розширених полів.domain – an optional domain to set on candidate values on the client side (domain or a python expression that will be evaluated to provide domain)
context (dict) – необов’язковий контекст для використання на стороні клієнта під час роботи з цим полем
ondelete (str) – що робити при видаленні запису, на який є посилання; можливі значення
'set null'
,'restrict'
,'cascade'
auto_join (bool) – чи створюються об’єднання при пошуку за цим полем (за замовчуванням:
False
)delegate (bool) – встановлює значення
True
, щоб зробити поля цільової моделі доступними з поточної моделі (відповідає_inherits
)check_company (bool) – Позначте поле для перевірки у
_check_company()
. Додайте домен компанії за замовчуванням залежно від атрибутів поля.
- class odoo.fields.One2many[source]¶
Поле типу one2many; значенням такого поля є набір записів усіх записів у
comodel_name
таких, що полеinverse_name
дорівнює поточному запису.- Параметри
comodel_name (str) – назва цільової моделі
inverse_name (str) – назва зворотного поля
Many2one
вcomodel_name
domain – an optional domain to set on candidate values on the client side (domain or a python expression that will be evaluated to provide domain)
context (dict) – необов’язковий контекст для використання на стороні клієнта під час роботи з цим полем
auto_join (bool) – чи створюються об’єднання при пошуку за цим полем (за замовчуванням:
False
)
Атрибути
comodel_name
таinverse_name
є обов’язковими, окрім випадків пов’язаних полів або розширень полів.
- class odoo.fields.Many2many[source]¶
Поле many2many; значенням такого поля є набір записів.
- Параметри
comodel_name – назва цільової моделі (рядок) обов’язкова, за винятком випадку пов’язаних або розширених полів
relation (str) – необов’язкова назва таблиці, яка зберігає відношення в базі даних
column1 (str) – необов’язкова назва стовпця, що посилається на «ці» записи в таблиці
relation
column2 (str) – необов’язкова назва стовпця, що посилається на «ті» записи в таблиці
relation
Атрибути
relation
,column1
таcolumn2
є необов’язковими. Якщо їх не задано, імена автоматично генеруються з імен моделей, якщоmodel_name
таcomodel_name
відрізняються!Зауважте, що наявність декількох полів з неявними параметрами зв’язку на даній моделі з тією самою комоделлю не сприймається ORM, оскільки ці поля будуть використовувати ту саму таблицю. ORM не дозволяє двом полям типу many2many використовувати однакові параметри зв’язку, за винятком випадків, коли
в обох полях використовується однакова модель, комодель, і параметри зв’язку є явними; або
хоча б одне поле належить моделі з
_auto = False
.
- Параметри
domain – an optional domain to set on candidate values on the client side (domain or a python expression that will be evaluated to provide domain)
context (dict) – необов’язковий контекст для використання на стороні клієнта під час роботи з цим полем
check_company (bool) – Позначте поле для перевірки у
_check_company()
. Додайте домен компанії за замовчуванням залежно від атрибутів поля.
- class odoo.fields.Command[source]¶
One2many
andMany2many
fields expect a special command to manipulate the relation they implement.Internally, each command is a 3-elements tuple where the first element is a mandatory integer that identifies the command, the second element is either the related record id to apply the command on (commands update, delete, unlink and link) either 0 (commands create, clear and set), the third element is either the
values
to write on the record (commands create and update) either the newids
list of related records (command set), either 0 (commands delete, unlink, link, and clear).Via Python, we encourage developers craft new commands via the various functions of this namespace. We also encourage developers to use the command identifier constant names when comparing the 1st element of existing commands.
Via RPC, it is impossible nor to use the functions nor the command constant names. It is required to instead write the literal 3-elements tuple where the first element is the integer identifier of the command.
- CREATE = 0¶
- UPDATE = 1¶
- DELETE = 2¶
- UNLINK = 3¶
- LINK = 4¶
- CLEAR = 5¶
- SET = 6¶
- classmethod create(values: dict)[source]¶
Create new records in the comodel using
values
, link the created records toself
.In case of a
Many2many
relation, one unique new record is created in the comodel such that all records inself
are linked to the new record.In case of a
One2many
relation, one new record is created in the comodel for every record inself
such that every record inself
is linked to exactly one of the new records.Return the command triple
(CREATE, 0, values)
- classmethod update(id: int, values: dict)[source]¶
Write
values
on the related record.Return the command triple
(UPDATE, id, values)
- classmethod delete(id: int)[source]¶
Remove the related record from the database and remove its relation with
self
.In case of a
Many2many
relation, removing the record from the database may be prevented if it is still linked to other records.Return the command triple
(DELETE, id, 0)
- classmethod unlink(id: int)[source]¶
Remove the relation between
self
and the related record.In case of a
One2many
relation, the given record is deleted from the database if the inverse field is set asondelete='cascade'
. Otherwise, the value of the inverse field is set to False and the record is kept.Return the command triple
(UNLINK, id, 0)
- classmethod link(id: int)[source]¶
Add a relation between
self
and the related record.Return the command triple
(LINK, id, 0)
Псевдореляційні поля¶
- class odoo.fields.Reference[source]¶
Псевдореляційне поле (немає FK в базі даних).
Значення поля зберігається у базі даних у вигляді рядка
string
за зразком"res_model,res_id"
.
- class odoo.fields.Many2oneReference[source]¶
Псевдореляційне поле (немає FK в базі даних).
Значення поля зберігається в базі даних як
integer
id.На відміну від полів
Посилання
, модель має бути вказана у поліChar
, назва якого має бути вказана в атрибутіmodel_field
для поточного поляMany2oneReference
.
Обчислювані поля¶
Поля можна обчислити (замість читання безпосередньо з бази даних) за допомогою параметра compute
. Він повинен призначити обчислене значення полю. Якщо він використовує значення інших полів, він має вказати ці поля за допомогою depends()
.
from odoo import api
total = fields.Float(compute='_compute_total')
@api.depends('value', 'tax')
def _compute_total(self):
for record in self:
record.total = record.value + record.value * record.tax
залежності можуть бути виділені пунктиром, якщо використовуються підполя:
@api.depends('line_ids.value') def _compute_total(self): for record in self: record.total = sum(line.value for line in record.line_ids)
обчислені поля не зберігаються за замовчуванням, вони обчислюються та повертаються за запитом. Встановлення
store=True
зберігатиме їх у базі даних і автоматично вмикатиме пошук.пошук у обчисленому полі також можна ввімкнути, встановивши параметр
search
. Значенням є назва методу, що повертає Пошук доменів.upper_name = field.Char(compute='_compute_upper', search='_search_upper') def _search_upper(self, operator, value): if operator == 'like': operator = 'ilike' return [('name', operator, value)]
Метод пошуку викликається під час обробки доменів перед виконанням фактичного пошуку в моделі. Він має повертати домен, еквівалентний умові:
значення оператора поля
.
За замовчуванням обчислені поля доступні лише для читання. Щоб дозволити встановлювати значення в обчисленому полі, використовуйте параметр
inverse
. Це назва функції, яка повертає обчислення та встановлює відповідні поля:document = fields.Char(compute='_get_document', inverse='_set_document') def _get_document(self): for record in self: with open(record.get_document_path) as f: record.document = f.read() def _set_document(self): for record in self: if not record.document: continue with open(record.get_document_path()) as f: f.write(record.document)
кілька полів можна обчислювати одночасно одним і тим же методом, просто використовуйте той самий метод для всіх полів і встановіть усі:
discount_value = fields.Float(compute='_apply_discount') total = fields.Float(compute='_apply_discount') @api.depends('value', 'discount') def _apply_discount(self): for record in self: # compute actual discount from discount percentage discount = record.value * record.discount record.discount_value = discount record.total = record.value - discount
Попередження
Хоча можна використовувати той самий метод обчислення для кількох полів, не рекомендується робити те саме для зворотного методу.
Під час обчислення інверсії всі поля, які використовують згадану інверсію, захищені, тобто їх неможливо обчислити, навіть якщо їх значення немає в кеші.
Якщо здійснюється доступ до будь-якого з цих полів, а його значення немає в кеші, ORM просто поверне значення за замовчуванням False
для цих полів. Це означає, що значення інверсних полів (окрім того, яке запускає інверсний метод) може не давати правильне значення, і це, ймовірно, порушить очікувану поведінку інверсного методу.
Автоматичні поля¶
Поля журналу доступу¶
Ці поля встановлюються та оновлюються автоматично, якщо _log_access
увімкнено. Його можна вимкнути, щоб уникнути створення або оновлення тих полів у таблицях, для яких вони не є корисними.
За замовчуванням _log_access
має те саме значення, що й _auto
Попередження
_log_access
має бути включений на TransientModel
.
Зарезервовані назви полів¶
Кілька назв полів зарезервовано для попередньо визначеної поведінки, крім автоматизованих полів. Вони повинні бути визначені в моделі, коли потрібна відповідна поведінка:
- Model.name¶
значення за замовчуванням для
_rec_name
, яке використовується для відображення записів у контексті, де потрібне репрезентативне «іменування».
- Model.active¶
перемикає загальну видимість запису, якщо
active
встановлено наFalse
, запис невидимий у більшості пошуків і списків.Special methods:
- Model.action_archive()[source]¶
Sets
active
toFalse
on a recordset, by callingtoggle_active()
on its currently active records.
- Model.action_unarchive()[source]¶
Sets
active
toTrue
on a recordset, by callingtoggle_active()
on its currently inactive records.
- Model.parent_id¶
default_value
_parent_name
, що використовується для організації записів у структурі дерева та дозволяє операториchild_of
іparent_of
у доменах.
- Model.parent_path¶
Коли
_parent_store
має значення True, використовується для збереження значення, що відображає структуру дерева_parent_name
, і для оптимізації операторівchild_of
іparent_of
у домени пошуку. Для належної роботи він має бути оголошений за допомогоюindex=True
.
- Model.company_id¶
Основна назва поля, яке використовується для поведінки мульти-компаній Odoo.
Використовується
:meth:~odoo.models._check_company
для перевірки узгодженості мульти-компаній. Визначає, чи є запис спільною для компаній (без значення) чи доступний лише користувачам певної компанії.Many2one
:type:res_company
Набори записів¶
Взаємодія з моделями та записами здійснюється через набори записів, упорядковану колекцію записів однієї моделі.
Попередження
На відміну від назви, наразі набори записів можуть містити дублікати. Це може змінитися в майбутньому.
Методи, визначені в моделі, виконуються в наборі записів, а їх self
є набором записів:
class AModel(models.Model):
_name = 'a.model'
def a_method(self):
# self can be anything between 0 records and all records in the
# database
self.do_operation()
Ітерація набору записів дасть нові набори з одного запису («singletons»), подібно до того, як ітерація рядка Python дає рядки з окремих символів:
def do_operation(self):
print(self) # => a.model(1, 2, 3, 4, 5)
for record in self:
print(record) # => a.model(1), then a.model(2), then a.model(3), ...
Доступ до поля¶
Набори записів забезпечують інтерфейс «Активний запис»: поля моделі можна читати та записувати безпосередньо із запису як атрибути.
Примітка
Під час доступу до нереляційних полів у наборі записів із потенційно кількох записів використовуйте mapped()
:
total_qty = sum(self.mapped('qty'))
До значень полів також можна отримати доступ, як до елементів dict, що елегантніше та безпечніше, ніж getattr()
для динамічних назв полів. Встановлення значення поля запускає оновлення бази даних:
>>> record.name
Example Name
>>> record.company_id.name
Company Name
>>> record.name = "Bob"
>>> field = "name"
>>> record[field]
Bob
Попередження
Спроба прочитати поле в кількох записах призведе до помилки для нереляційних полів.
Доступ до реляційного поля (Many2one
, One2many
, Many2many
) завжди повертає набір записів, порожній, якщо поле не встановлено.
Кешування записів і попередня вибірка¶
Odoo підтримує кеш-пам’ять для полів записів, тому не кожен доступ до поля видає запит бази даних, що було б жахливим для продуктивності. Наведений нижче приклад запитує базу даних лише для першого оператора:
record.name # first access reads value from database
record.name # second access gets value from cache
Щоб уникнути читання одного поля в одному записі за раз, Odoo попередньо вибирає записи та поля, дотримуючись певних евристик, щоб отримати хорошу продуктивність. Після того, як поле має бути прочитано в заданому записі, ORM фактично зчитує це поле у більшому наборі записів і зберігає повернуті значення в кеші для подальшого використання. Попередньо вибраний набір записів зазвичай є набором записів, з якого запис надходить ітерацією. Крім того, усі прості збережені поля (логічне, ціле, float, char, text, date, datetime, selection, many2one) вибираються в цілому; вони відповідають стовпцям таблиці моделі та ефективно витягуються в одному запиті.
Розглянемо наступний приклад, де partners
- це набір із 1000 записів. Без попередньої вибірки цикл зробить 2000 запитів до бази даних. З попередньою вибіркою виконується лише один запит:
for partner in partners:
print partner.name # first pass prefetches 'name' and 'lang'
# (and other fields) on all 'partners'
print partner.lang
Попередня вибірка також працює з вторинними записами: коли зчитуються реляційні поля, їхні значення (які є записами) підписуються для майбутньої попередньої вибірки. Доступ до одного з цих вторинних записів попередньо завантажує всі вторинні записи з тієї самої моделі. Таким чином у наведеному нижче прикладі створюються лише два запити, один для партнерів і один для країн:
countries = set()
for partner in partners:
country = partner.country_id # first pass prefetches all partners
countries.add(country.name) # first pass prefetches all countries
Перегляньте також
The methods search_fetch()
and
fetch()
can be used to populate the cache of
records, typically in cases where the prefetching mechanism does not work
well.
Декоратори методів¶
Модуль Odoo API визначає середовища Odoo та декоратори методів.
- odoo.api.model(method)[source]¶
Прикрасьте метод у стилі запису, де
self
є набором записів, але його вміст нерелевантний, має значення лише модель. Такий спосіб:@api.model def method(self, args): ...
- odoo.api.constrains(*args)[source]¶
Прикрасьте перевірку обмежень.
Кожен аргумент має бути назвою поля, яке використовується під час перевірки:
@api.constrains('name', 'description') def _check_description(self): for record in self: if record.name == record.description: raise ValidationError("Fields name and description must be different")
Викликається для записів, у яких одне з названих полів було змінено.
Має викликати
ValidationError
, якщо перевірка не вдалася.Попередження
@constrains
підтримує лише прості назви полів, назви з крапками (поля реляційних полів, наприкладpartner_id.customer
) не підтримуються та ігноруватимуться.@constrains
запускатиметься, лише якщо оголошені поля в декорованому методі включено до викликуcreate
абоwrite
. Це означає, що поля, яких немає в поданні, не викликатимуть виклик під час створення запису. Перевизначенняcreate
необхідне, щоб переконатися, що обмеження завжди запускатиметься (наприклад, щоб перевірити відсутність значення).One may also pass a single function as argument. In that case, the field names are given by calling the function with a model instance.
- odoo.api.depends(*args)[source]¶
Повертає декоратор, який визначає залежності полів методу «обчислення» (для полів функцій нового стилю). Кожен аргумент має бути рядком, який складається з послідовності назв полів, розділених крапками:
pname = fields.Char(compute='_compute_pname') @api.depends('partner_id.name', 'partner_id.is_company') def _compute_pname(self): for record in self: if record.partner_id.is_company: record.pname = (record.partner_id.name or "").upper() else: record.pname = record.partner_id.name
Можна також передати одну функцію як аргумент. У цьому випадку залежності задаються шляхом виклику функції з моделлю поля.
- odoo.api.onchange(*args)[source]¶
Повертає декоратор, щоб прикрасити метод onchange для заданих полів.
У представленнях форми, де з’являється поле, метод буде викликано, коли одне з заданих полів буде змінено. Метод викликається для псевдозапису, який містить значення, присутні у формі. Призначення полів у цьому записі автоматично надсилаються назад клієнту.
Кожен аргумент має бути назвою поля:
@api.onchange('partner_id') def _onchange_partner(self): self.message = "Dear %s" % (self.partner_id.name or "")
return { 'warning': {'title': "Warning", 'message': "What is this?", 'type': 'notification'}, }
Якщо для типу встановлено сповіщення, попередження відображатиметься в сповіщенні. Інакше він відображатиметься у діалоговому вікні за замовчуванням.
Попередження
@onchange
підтримує лише прості назви полів, крапкові імена (поля реляційних полів, наприклад,partner_id.tz
) не підтримуються і будуть проігнорованіНебезпека
Оскільки
@onchange
повертає набір псевдозаписів, виклик будь-якого з методів CRUD (create()
,read()
,write()
,unlink()
) для цього набору записів є невизначеною поведінкою, оскільки вони потенційно ще не існують в базі даних.Замість цього просто встановіть поле запису, як показано у прикладі вище, або викличте метод
update()
.Попередження
Поле
one2many
абоmany2many
не може модифікувати себе через onchange. Це обмеження веб-клієнта - див. #2693.
- odoo.api.returns(model, downgrade=None, upgrade=None)[source]¶
Повертає декоратор для методів, які повертають екземпляри
model
.- Параметри
model – назва моделі або
'self
для поточної моделіdowngrade – функція
downgrade(self, value, *args, **kwargs)
для перетворенняvalue
у стилі запису на вивід у традиційному стиліupgrade – функцію
upgrade(self, value, *args, **kwargs)
для перетворенняvalue
у традиційному стилі у вивід у стилі запису
Аргументи
self
,*args
і**kwargs
- це ті, що передаються в метод у стилі запису.Декоратор адаптує вивід методу до стилю api:
id
,ids
абоFalse
для традиційного стилю, і recordset для стилю записів:@model @returns('res.partner') def find_partner(self, arg): ... # return some record # output depends on call style: traditional vs record style partner_id = model.find_partner(cr, uid, arg, context=context) # recs = model.browse(cr, uid, ids, context) partner_record = recs.find_partner(arg)
Зверніть увагу, що декорований метод повинен задовольняти цій умові.
Ці декоратори автоматично успадковуються: метод, який перевизначає декорований існуючий метод, буде декорований тим самим
@returns(model)
.
- odoo.api.autovacuum(method)[source]¶
Прикрасьте метод так, щоб він викликався щоденним завданням cron прибирання (модель
ir.autovacuum
). Зазвичай це використовується для завдань на кшталт збирання сміття, які не заслуговують на окреме завдання cron.
- odoo.api.depends_context(*args)[source]¶
Повертає декоратор, який визначає контекстні залежності незбереженого «обчислювального» методу. Кожен аргумент є ключем у словнику контексту:
price = fields.Float(compute='_compute_product_price') @api.depends_context('pricelist') def _compute_product_price(self): for product in self: if product.env.context.get('pricelist'): pricelist = self.env['product.pricelist'].browse(product.env.context['pricelist']) else: pricelist = self.env['product.pricelist'].get_default_pricelist() product.price = pricelist._get_products_price(product).get(product.id, 0.0)
Усі залежності мають бути хешованими. Наступні ключі мають спеціальну підтримку:
company
(значення в контексті або поточний ідентифікатор компанії),uid
(поточний id користувача та прапор суперкористувача),active_test
(значення в env.context або значення в field.context).
- odoo.api.model_create_multi(method)[source]¶
Прикрасьте метод, який приймає список словників і створює кілька записів. Метод може бути викликаний або з одним dict, або зі списком dicts:
record = model.create(vals) records = model.create([vals, ...])
- odoo.api.ondelete(*, at_uninstall)[source]¶
Mark a method to be executed during
unlink()
.The goal of this decorator is to allow client-side errors when unlinking records if, from a business point of view, it does not make sense to delete such records. For instance, a user should not be able to delete a validated sales order.
While this could be implemented by simply overriding the method
unlink
on the model, it has the drawback of not being compatible with module uninstallation. When uninstalling the module, the override could raise user errors, but we shouldn’t care because the module is being uninstalled, and thus all records related to the module should be removed anyway.This means that by overriding
unlink
, there is a big chance that some tables/records may remain as leftover data from the uninstalled module. This leaves the database in an inconsistent state. Moreover, there is a risk of conflicts if the module is ever reinstalled on that database.Methods decorated with
@ondelete
should raise an error following some conditions, and by convention, the method should be named either_unlink_if_<condition>
or_unlink_except_<not_condition>
.@api.ondelete(at_uninstall=False) def _unlink_if_user_inactive(self): if any(user.active for user in self): raise UserError("Can't delete an active user!") # same as above but with _unlink_except_* as method name @api.ondelete(at_uninstall=False) def _unlink_except_active_user(self): if any(user.active for user in self): raise UserError("Can't delete an active user!")
- Параметри
at_uninstall (bool) – Whether the decorated method should be called if the module that implements said method is being uninstalled. Should almost always be
False
, so that module uninstallation does not trigger those errors.
Небезпека
The parameter
at_uninstall
should only be set toTrue
if the check you are implementing also applies when uninstalling the module.For instance, it doesn’t matter if when uninstalling
sale
, validated sales orders are being deleted because all data pertaining tosale
should be deleted anyway, in that caseat_uninstall
should be set toFalse
.However, it makes sense to prevent the removal of the default language if no other languages are installed, since deleting the default language will break a lot of basic behavior. In this case,
at_uninstall
should be set toTrue
.
Середовище¶
- class odoo.api.Environment(cr, uid, context, su=False, uid_origin=None)[source]¶
The environment stores various contextual data used by the ORM:
cr
: the current database cursor (for database queries);uid
: the current user id (for access rights checks);context
: the current context dictionary (arbitrary metadata);su
: whether in superuser mode.
It provides access to the registry by implementing a mapping from model names to models. It also holds a cache for records, and a data structure to manage recomputations.
>>> records.env
<Environment object ...>
>>> records.env.uid
3
>>> records.env.user
res.user(3)
>>> records.env.cr
<Cursor object ...>
When creating a recordset from an other recordset, the environment is inherited. The environment can be used to get an empty recordset in an other model, and query that model:
>>> self.env['res.partner']
res.partner()
>>> self.env['res.partner'].search([('is_company', '=', True), ('customer', '=', True)])
res.partner(7, 18, 12, 14, 17, 19, 8, 31, 26, 16, 13, 20, 30, 22, 29, 15, 23, 28, 74)
Some lazy properties are available to access the environment (contextual) data:
- Environment.user¶
Return the current user (as an instance).
- Повертає
current user - sudoed
- Тип повернення
res.users record
- Environment.company¶
Return the current company (as an instance).
If not specified in the context (
allowed_company_ids
), fallback on current user main company.- Викликає
AccessError – invalid or unauthorized
allowed_company_ids
context key content.- Повертає
current company (default=`self.user.company_id`), with the current environment
- Тип повернення
res.company record
Попередження
No sanity checks applied in sudo mode! When in sudo mode, a user can access any company, even if not in his allowed companies.
This allows to trigger inter-company modifications, even if the current user doesn’t have access to the targeted company.
- Environment.companies¶
Return a recordset of the enabled companies by the user.
If not specified in the context(
allowed_company_ids
), fallback on current user companies.- Викликає
AccessError – invalid or unauthorized
allowed_company_ids
context key content.- Повертає
current companies (default=`self.user.company_ids`), with the current environment
- Тип повернення
res.company recordset
Попередження
No sanity checks applied in sudo mode ! When in sudo mode, a user can access any company, even if not in his allowed companies.
This allows to trigger inter-company modifications, even if the current user doesn’t have access to the targeted company.
Useful environment methods¶
- Environment.ref(xml_id, raise_if_not_found=True)[source]¶
Повернути запис, що відповідає заданому
xml_id
.- Параметри
- Повертає
Found record or None
- Викликає
ValueError – if record wasn’t found and
raise_if_not_found
is True
Зміна середовища¶
- Model.with_context([context][, **overrides]) Model [source]¶
Returns a new version of this recordset attached to an extended context.
The extended context is either the provided
context
in whichoverrides
are merged or the current context in whichoverrides
are merged e.g.:# current context is {'key1': True} r2 = records.with_context({}, key2=True) # -> r2._context is {'key2': True} r2 = records.with_context(key2=True) # -> r2._context is {'key1': True, 'key2': True}
- Model.with_user(user)[source]¶
Return a new version of this recordset attached to the given user, in non-superuser mode, unless
user
is the superuser (by convention, the superuser is always in superuser mode.)
- Model.with_company(company)[source]¶
Return a new version of this recordset with a modified context, such that:
result.env.company = company result.env.companies = self.env.companies | company
- Параметри
company (
res_company
or int) – головна компанія нового середовища.
Попередження
When using an unauthorized company for current user, accessing the company(ies) on the environment may trigger an AccessError if not done in a sudoed environment.
- Model.with_env(env)[source]¶
Return a new version of this recordset attached to the provided environment.
- Параметри
env (
Environment
) –
Примітка
The returned recordset has the same prefetch object as
self
.
- Model.sudo([flag=True])[source]¶
Returns a new version of this recordset with superuser mode enabled or disabled, depending on
flag
. The superuser mode does not change the current user, and simply bypasses access rights checks.Попередження
Using
sudo
could cause data access to cross the boundaries of record rules, possibly mixing records that are meant to be isolated (e.g. records from different companies in multi-company environments).It may lead to un-intuitive results in methods which select one record among many - for example getting the default company, or selecting a Bill of Materials.
Примітка
The returned recordset has the same prefetch object as
self
.
Виконання SQL¶
Атрибут cr
в середовищах є курсором для поточної транзакції бази даних і дозволяє безпосередньо виконувати SQL або для запитів, які важко виразити за допомогою ORM (наприклад, складні об’єднання), або для продуктивності причини:
self.env.cr.execute("some_sql", params)
Попередження
Executing raw SQL bypasses the ORM and, by consequent, Odoo security rules. Please make sure your queries are sanitized when using user input and prefer using ORM utilities if you don’t really need to use SQL queries.
The recommended way to build SQL queries is to use the wrapper object
- class odoo.tools.SQL(code: str | odoo.tools.sql.SQL = '', /, *args, **kwargs)[source]¶
An object that wraps SQL code with its parameters, like:
sql = SQL("UPDATE TABLE foo SET a = %s, b = %s", 'hello', 42) cr.execute(sql)
The code is given as a
%
-format string, and supports either positional arguments (with%s
) or named arguments (with%(name)s
). Escaped characters (like"%%"
) are not supported, though. The arguments are meant to be merged into the code using the%
formatting operator.The SQL wrapper is designed to be composable: the arguments can be either actual parameters, or SQL objects themselves:
sql = SQL( "UPDATE TABLE %s SET %s", SQL.identifier(tablename), SQL("%s = %s", SQL.identifier(columnname), value), )
The combined SQL code is given by
sql.code
, while the corresponding combined parameters are given by the listsql.params
. This allows to combine any number of SQL terms without having to separately combine their parameters, which can be tedious, bug-prone, and is the main downside ofpsycopg2.sql <https://www.psycopg.org/docs/sql.html>
.The second purpose of the wrapper is to discourage SQL injections. Indeed, if
code
is a string literal (not a dynamic string), then the SQL object made withcode
is guaranteed to be safe, provided the SQL objects within its parameters are themselves safe.- join(args: Iterable) odoo.tools.sql.SQL [source]¶
Join SQL objects or parameters with
self
as a separator.
- classmethod identifier(name: str, subname: Optional[str] = None) odoo.tools.sql.SQL [source]¶
Return an SQL object that represents an identifier.
One important thing to know about models is that they don’t necessarily perform database updates right away. Indeed, for performance reasons, the framework delays the recomputation of fields after modifying records. And some database updates are delayed, too. Therefore, before querying the database, one has to make sure that it contains the relevant data for the query. This operation is called flushing and performs the expected database updates.
Example
# make sure that 'partner_id' is up-to-date in database
self.env['model'].flush_model(['partner_id'])
self.env.cr.execute(SQL("SELECT id FROM model WHERE partner_id IN %s", ids))
ids = [row[0] for row in self.env.cr.fetchall()]
Before every SQL query, one has to flush the data needed for that query. There are three levels for flushing, each with its own API. One can flush either everything, all the records of a model, or some specific records. Because delaying updates improves performance in general, we recommend to be specific when flushing.
- Model.flush_model(fnames=None)[source]¶
Process the pending computations and database updates on
self
’s model. When the parameter is given, the method guarantees that at least the given fields are flushed to the database. More fields can be flushed, though.- Параметри
fnames – optional iterable of field names to flush
- Model.flush_recordset(fnames=None)[source]¶
Process the pending computations and database updates on the records
self
. When the parameter is given, the method guarantees that at least the given fields on recordsself
are flushed to the database. More fields and records can be flushed, though.- Параметри
fnames – optional iterable of field names to flush
Оскільки моделі використовують той самий курсор, а Environment
містить різні кеші, ці кеші мають бути визнані недійсними під час зміни бази даних у необробленому SQL, інакше подальше використання моделей може стати неузгодженим. Необхідно очищати кеші під час використання CREATE
, UPDATE
або DELETE
у SQL, але не SELECT
(який просто читає базу даних).
Example
# make sure 'state' is up-to-date in database
self.env['model'].flush_model(['state'])
self.env.cr.execute("UPDATE model SET state=%s WHERE state=%s", ['new', 'old'])
# invalidate 'state' from the cache
self.env['model'].invalidate_model(['state'])
Just like flushing, one can invalidate either the whole cache, the cache of all the records of a model, or the cache of specific records. One can even invalidate specific fields on some records or all records of a model. As the cache improves performance in general, we recommend to be specific when invalidating.
- Environment.invalidate_all(flush=True)[source]¶
Invalidate the cache of all records.
- Параметри
flush – whether pending updates should be flushed before invalidation. It is
True
by default, which ensures cache consistency. Do not use this parameter unless you know what you are doing.
- Model.invalidate_model(fnames=None, flush=True)[source]¶
Invalidate the cache of all records of
self
’s model, when the cached values no longer correspond to the database values. If the parameter is given, only the given fields are invalidated from cache.- Параметри
fnames – optional iterable of field names to invalidate
flush – whether pending updates should be flushed before invalidation. It is
True
by default, which ensures cache consistency. Do not use this parameter unless you know what you are doing.
- Model.invalidate_recordset(fnames=None, flush=True)[source]¶
Invalidate the cache of the records in
self
, when the cached values no longer correspond to the database values. If the parameter is given, only the given fields onself
are invalidated from cache.- Параметри
fnames – optional iterable of field names to invalidate
flush – whether pending updates should be flushed before invalidation. It is
True
by default, which ensures cache consistency. Do not use this parameter unless you know what you are doing.
The methods above keep the caches and the database consistent with each other. However, if computed field dependencies have been modified in the database, one has to inform the models for the computed fields to be recomputed. The only thing the framework needs to know is what fields have changed on which records.
Example
# make sure 'state' is up-to-date in database
self.env['model'].flush_model(['state'])
# use the RETURNING clause to retrieve which rows have changed
self.env.cr.execute("UPDATE model SET state=%s WHERE state=%s RETURNING id", ['new', 'old'])
ids = [row[0] for row in self.env.cr.fetchall()]
# invalidate the cache, and notify the update to the framework
records = self.env['model'].browse(ids)
records.invalidate_recordset(['state'])
records.modified(['state'])
One has to figure out which records have been modified. There are many ways to
do this, possibly involving extra SQL queries. In the example above, we take
advantage of the RETURNING
clause of PostgreSQL to retrieve the information
without an extra query. After making the cache consistent by invalidation,
invoke the method modified
on the modified records with the fields that
have been updated.
- Model.modified(fnames, create=False, before=False)[source]¶
Notify that fields will be or have been modified on
self
. This invalidates the cache where necessary, and prepares the recomputation of dependent stored fields.- Параметри
fnames – iterable of field names modified on records
self
create – whether called in the context of record creation
before – whether called before modifying records
self
Поширені методи ORM¶
Створити/оновити¶
- Model.create(vals_list) records [source]¶
Створює нові записи для моделі.
The new records are initialized using the values from the list of dicts
vals_list
, and if necessary those fromdefault_get()
.- Параметри
vals_list (Union[list[dict], dict]) –
делегування деяких полів моделі записам, які вона містить:
[{'field_name': field_value, ...}, ...]
For backward compatibility,
vals_list
may be a dictionary. It is treated as a singleton list[vals]
, and a single record is returned.see
write()
for details- Повертає
the created records
- Викликає
AccessError – if the current user is not allowed to create records of the specified model
ValidationError – if user tries to enter invalid value for a selection field
ValueError – if a field name specified in the create values does not exist.
UserError – if a loop would be created in a hierarchy of objects a result of the operation (such as setting an object as its own parent)
- Model.copy(default=None)[source]¶
Duplicate record
self
updating it with default values- Параметри
default (dict) – dictionary of field values to override in the original values of the copied record, e.g:
{'field_name': overridden_value, ...}
- Повертає
new record
- Model.default_get(fields_list) default_values [source]¶
Return default values for the fields in
fields_list
. Default values are determined by the context, user defaults, and the model itself.- Параметри
fields_list (list) – names of field whose default is requested
- Повертає
словник, що зіставляє назви полів із відповідними значеннями за замовчуванням, якщо вони мають значення за замовчуванням.
- Тип повернення
Примітка
Unrequested defaults won’t be considered, there is no need to return a value for fields whose names are not in
fields_list
.
- Model.name_create(name) record [source]¶
Create a new record by calling
create()
with only one value provided: the display name of the new record.The new record will be initialized with any default values applicable to this model, or provided through the context. The usual behavior of
create()
applies.- Параметри
name – display name of the record to create
- Тип повернення
- Повертає
the (id, display_name) pair value of the created record
- Model.write(vals)[source]¶
Updates all records in
self
with the provided values.- Параметри
vals (dict) – fields to update and the value to set on them
- Викликає
AccessError – if user is not allowed to modify the specified records/fields
ValidationError – if invalid values are specified for selection fields
UserError – if a loop would be created in a hierarchy of objects a result of the operation (such as setting an object as its own parent)
Для числових полів (
Integer
,Float
) значення має бути відповідного типуДля
Selection
значення має відповідати значенням вибору (зазвичайstr
, інодіint
)Для
Many2one
значення має бути ідентифікатором бази даних запису, який потрібно встановитиThe expected value of a
One2many
orMany2many
relational field is a list ofCommand
that manipulate the relation the implement. There are a total of 7 commands:create()
,update()
,delete()
,unlink()
,link()
,clear()
, andset()
.For
Date
and~odoo.fields.Datetime
, the value should be either a date(time), or a string.Попередження
If a string is provided for Date(time) fields, it must be UTC-only and formatted according to
odoo.tools.misc.DEFAULT_SERVER_DATE_FORMAT
andodoo.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT
Інші нереляційні поля використовують рядок для значення
Пошук/Читання¶
- Model.browse([ids]) records [source]¶
Повертає набір записів для ідентифікаторів, наданих як параметр у поточному середовищі.
self.browse([7, 18, 12]) res.partner(7, 18, 12)
- Model.search(domain[, offset=0][, limit=None][, order=None])[source]¶
Search for the records that satisfy the given
domain
search domain.- Параметри
domain – Домен пошуку. Використовуйте порожній список, щоб зіставити всі записи.
offset (int) – кількість результатів для ігнорування (за замовчуванням: немає)
limit (int) – максимальна кількість записів для повернення (за замовчуванням: усі)
order (str) – рядок сортування
- Повертає
щонайбільше
limit
записів, що відповідають критеріям пошуку- Викликає
AccessError – if user is not allowed to access requested information
This is a high-level method, which should not be overridden. Its actual implementation is done by method
_search()
.
- Model.search_count(domain[, limit=None]) int [source]¶
Повертає кількість записів у поточній моделі, які відповідають наданому домену.
- Параметри
domain – Домен пошуку. Використовуйте порожній список, щоб зіставити всі записи.
limit – maximum number of record to count (upperbound) (default: all)
This is a high-level method, which should not be overridden. Its actual implementation is done by method
_search()
.
- Model.search_fetch(domain, field_names[, offset=0][, limit=None][, order=None])[source]¶
Search for the records that satisfy the given
domain
search domain, and fetch the given fields to the cache. This method is like a combination of methodssearch()
andfetch()
, but it performs both tasks with a minimal number of SQL queries.- Параметри
domain – Домен пошуку. Використовуйте порожній список, щоб зіставити всі записи.
field_names – a collection of field names to fetch
offset (int) – кількість результатів для ігнорування (за замовчуванням: немає)
limit (int) – максимальна кількість записів для повернення (за замовчуванням: усі)
order (str) – рядок сортування
- Повертає
щонайбільше
limit
записів, що відповідають критеріям пошуку- Викликає
AccessError – if user is not allowed to access requested information
- Model.name_search(name='', args=None, operator='ilike', limit=100) records [source]¶
Шукайте записи, які мають відображувану назву, що відповідає даному шаблону
name
у порівнянні з данимoperator
, а також відповідає додатковому домену пошуку (args
).This is used for example to provide suggestions based on a partial value for a relational field. Should usually behave as the reverse of
display_name
, but that is not guaranteed.This method is equivalent to calling
search()
with a search domain based ondisplay_name
and mapping id and display_name on the resulting search.- Параметри
- Тип повернення
- Повертає
list of pairs
(id, display_name)
for all matching records.
- Model.fetch(field_names)[source]¶
Make sure the given fields are in memory for the records in
self
, by fetching what is necessary from the database. Non-stored fields are mostly ignored, except for their stored dependencies. This method should be called to optimize code.- Параметри
field_names – a collection of field names to fetch
- Викликає
AccessError – if user is not allowed to access requested information
This method is implemented thanks to methods
_search()
and_fetch_query()
, and should not be overridden.
- Model.read([fields])[source]¶
Read the requested fields for the records in
self
, and return their values as a list of dicts.- Параметри
- Повертає
список словників, що відображають назви полів на їхні значення, з одним словником на запис
- Тип повернення
- Викликає
AccessError – if user is not allowed to access requested information
ValueError – if a requested field does not exist
This is a high-level method that is not supposed to be overridden. In order to modify how fields are read from database, see methods
_fetch_query()
and_read_format()
.
- Model._read_group(domain, groupby=(), aggregates=(), having=(), offset=0, limit=None, order=None)[source]¶
Get fields aggregations specified by
aggregates
grouped by the givengroupby
fields where record are filtered by thedomain
.- Параметри
domain (list) – Домен пошуку. Використовуйте порожній список, щоб зіставити всі записи.
groupby (list) – list of groupby descriptions by which the records will be grouped. A groupby description is either a field (then it will be grouped by that field) or a string
'field:granularity'
. Right now, the only supported granularities are'day'
,'week'
,'month'
,'quarter'
or'year'
, and they only make sense for date/datetime fields.aggregates (list) – list of aggregates specification. Each element is
'field:agg'
(aggregate field with aggregation function'agg'
). The possible aggregation functions are the ones provided by PostgreSQL,'count_distinct'
with the expected meaning and'recordset'
to act like'array_agg'
converted into a recordset.having (list) – A domain where the valid «fields» are the aggregates.
offset (int) – optional number of groups to skip
limit (int) – optional max number of groups to return
order (str) – optional
order by
specification, for overriding the natural sort ordering of the groups, see alsosearch()
.
- Повертає
list of tuple containing in the order the groups values and aggregates values (flatten):
[(groupby_1_value, ... , aggregate_1_value_aggregate, ...), ...]
. If group is related field, the value of it will be a recordset (with a correct prefetch set).- Тип повернення
- Викликає
AccessError – if user is not allowed to access requested information
- Model.read_group(domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True)[source]¶
Отримайте список записів у режимі перегляду списку, згрупованих за заданими полями
groupby
.- Параметри
domain (list) – Домен пошуку. Використовуйте порожній список, щоб зіставити всі записи.
fields (list) –
list of fields present in the list view specified on the object. Each element is either „field“ (field name, using the default aggregation), or „field:agg“ (aggregate field with aggregation function „agg“), or „name:agg(field)“ (aggregate field with „agg“ and return it as „name“). The possible aggregation functions are the ones provided by PostgreSQL and „count_distinct“, with the expected meaning.
groupby (list) – list of groupby descriptions by which the records will be grouped. A groupby description is either a field (then it will be grouped by that field) or a string „field:granularity“. Right now, the only supported granularities are „day“, „week“, „month“, „quarter“ or „year“, and they only make sense for date/datetime fields.
offset (int) – optional number of groups to skip
limit (int) – optional max number of groups to return
orderby (str) – необов’язкова специфікація
order by
, щоб змінити природне впорядкування груп, див. такожsearch()
(наразі підтримується лише для полів many2one)lazy (bool) – if true, the results are only grouped by the first groupby and the remaining groupbys are put in the __context key. If false, all the groupbys are done in one call.
- Повертає
список словників (один словник для кожного запису), що містить:
значення полів, згрупованих за полями в аргументі
groupby
__domain: список кортежів із зазначенням критеріїв пошуку
__context: словник із аргументом типу
groupby
- __range: (date/datetime only) dictionary with field_name:granularity as keys
mapping to a dictionary with keys: «from» (inclusive) and «to» (exclusive) mapping to a string representation of the temporal bounds of the group
- Тип повернення
[{„field_name_1“: value, …}, …]
- Викликає
AccessError – if user is not allowed to access requested information
Поля¶
Пошук доменів¶
Домен - це список критеріїв, кожен критерій - це трійка (або список
, або кортеж
) (field_name, operator, value)
, де:
field_name
(str
)назва поля поточної моделі або обхід зв’язку через
Many2one
з використанням крапкової нотації, наприклад.'street'
або'partner_id.country'
operator
(str
)оператор, який використовується для порівняння
field_name
ізvalue
. Допустимі оператори:=
дорівнює
!=
не дорівнює
>
більше ніж
>=
більше або дорівнює
<
менше ніж
<=
менше або дорівнює
=?
не встановлено або дорівнює (повертає істину, якщо
value
абоNone
абоFalse
, інакше поводиться як=
)=like
відповідає
field_name
шаблонуvalue
. Підкреслення_
у шаблоні означає (відповідає) будь-якому окремому символу; знак відсотка%
відповідає будь-якому рядку з нуля або більше символів.like
співпадає
field_name
шаблону%value%
. Подібно до=like
, алеvalue
обертає „%“ перед пошуком відповідностіnot like
не відповідає шаблону
%value%
ilike
без урахування регістру
like
not ilike
без урахування регістру
not like
=ilike
без урахування регістру
=like
in
дорівнює будь-якому з елементів із
value
,value
має бути списком елементівnot in
не дорівнює всім елементам із
value
child_of
є дочірнім (нащадком) запису
value
(значення може бути або одним елементом, або списком елементів).Враховує семантику моделі (тобто після поля зв’язку, названого
_parent_name
).parent_of
є батьківським (висхідним) запису
value
(значення може бути як одним елементом, так і списком елементів).Враховує семантику моделі (тобто після поля зв’язку, названого
_parent_name
).any
matches if any record in the relationship traversal through
field_name
(Many2one
,One2many
, orMany2many
) satisfies the provided domainvalue
.not any
matches if no record in the relationship traversal through
field_name
(Many2one
,One2many
, orMany2many
) satisfies the provided domainvalue
.
value
тип змінної, має бути порівнянним (через
operator
) з іменованим полем.
Критерії домену можна комбінувати за допомогою логічних операторів у формі prefix:
'&'
логічне AND, операція за замовчуванням для комбінування наступних критеріїв. Arity 2 (використовує наступні 2 критерії або комбінації).
'|'
логічне OR, арність 2.
'!'
логічне NOT, арність 1.
Примітка
Здебільшого для заперечення комбінацій критеріїв. Окремий критерій, як правило, має негативну форму (наприклад,
=
->!=
,<
->>=
), що простіше, ніж заперечення позитивного .
Example
To search for partners named ABC, with a phone or mobile number containing 7620:
[('name', '=', 'ABC'),
'|', ('phone','ilike','7620'), ('mobile', 'ilike', '7620')]
To search sales orders to invoice that have at least one line with a product that is out of stock:
[('invoice_status', '=', 'to invoice'),
('order_line', 'any', [('product_id.qty_available', '<=', 0)])]
Від’єднати¶
- Model.unlink()[source]¶
Deletes the records in
self
.- Викликає
AccessError – if the user is not allowed to delete all the given records
UserError – якщо запис є властивістю за замовчуванням для інших записів
Запис (набір) інформації¶
- Model.ids¶
Повертає список фактичних ідентифікаторів записів, які відповідають
self
.
- odoo.models.env¶
Повертає середовище заданого набору записів.
- тип
- Model.exists() records [source]¶
Returns the subset of records in
self
that exist. It can be used as a test on records:if record.exists(): ...
Згідно з умовами, нові записи повертаються як існуючі.
- Model.ensure_one()[source]¶
Verify that the current recordset holds a single record.
- Викликає
odoo.exceptions.ValueError –
len(self) != 1
- Model.get_metadata()[source]¶
Повернути деякі метадані про дані записи.
- Повертає
список словників власності для кожного запитуваного запису
- Тип повернення
список словників з такими ключами:
id: object id
create_uid: користувач, який створив запис
create_date: дата створення запису
write_uid: останній користувач, який змінив запис
write_date: дата останньої зміни в записі
xmlid: XML ID для посилання на цей запис (якщо він є), у форматі
module.name
xmlids: list of dict with xmlid in format
module.name
, and noupdate as booleannoupdate: логічне значення, яке повідомляє, чи буде запис оновлено чи ні
Операції¶
Набори записів є незмінними, але набори однієї моделі можна комбінувати за допомогою різних операцій із наборами, повертаючи нові набори записів.
запис у наборі
повертає, чи присутнійзапис
(який має бути 1-елементним набором записів) унаборі
.запис не в наборі
є зворотною операцієюset1 <= set2
іset1 < set2
повертають, чи єset1
підмножиноюset2
(відповідно strict)set1 >= set2
іset1 > set2
повертають, чи єset1
надмножиноюset2
(відповідно strict)set1 | set2
повертає об’єднання двох наборів записів, новий набір записів, що містить усі записи, присутні в будь-якому джереліset1 & set2
повертає перетин двох наборів записів, новий набір записів, що містить лише записи, присутні в обох джерелахset1 - set2
повертає новий набір записів, що містить лише записиset1
, яких не вset2
Набори записів можна ітерувати, тому для трансформації доступні звичайні інструменти Python (map()
, sorted()
, ifilter()
, …), однак вони повертають або list
або iterator, видаляючи можливість викликати методи для їхнього результату або використовувати операції з наборами.
Тому набори записів забезпечують такі операції, повертаючи самі набори записів (якщо це можливо):
Фільтр¶
- Model.filtered(func)[source]¶
Повертає записи в
self
задовільнийfunc
.- Параметри
func (callable or str) – функція або послідовність назв полів, розділених крапками
- Повертає
набір записів, що задовольняє функцію func, може бути порожнім.
# only keep records whose company is the current user's records.filtered(lambda r: r.company_id == user.company_id) # only keep records whose partner is a company records.filtered("partner_id.is_company")
- Model.filtered_domain(domain)[source]¶
Return the records in
self
satisfying the domain and keeping the same order.- Параметри
domain – A search domain.
Карта¶
- Model.mapped(func)[source]¶
Застосуйте
func
до всіх записів уself
і поверніть результат як список або набір записів (якщоfunc
повертає набори записів). В останньому випадку порядок поверненого набору записів є довільним.- Параметри
func (callable or str) – функція або послідовність назв полів, розділених крапками
- Повертає
self, якщо func є помилковим, результат func застосовується до всіх записів
self
.- Тип повернення
list or recordset
# returns a list of summing two fields for each record in the set records.mapped(lambda r: r.field1 + r.field2)
Надана функція може бути рядком для отримання значень поля:
# returns a list of names records.mapped('name') # returns a recordset of partners records.mapped('partner_id') # returns the union of all partner banks, with duplicates removed records.mapped('partner_id.bank_ids')
Примітка
Починаючи з версії 13, підтримується мультиреляційний доступ до полів, який працює як відображений виклик:
records.partner_id # == records.mapped('partner_id')
records.partner_id.bank_ids # == records.mapped('partner_id.bank_ids')
records.partner_id.mapped('name') # == records.mapped('partner_id.name')
Сортувати¶
Grouping¶
- Model.grouped(key)[source]¶
Eagerly groups the records of
self
by thekey
, returning a dict from thekey
’s result to recordsets. All the resulting recordsets are guaranteed to be part of the same prefetch-set.Provides a convenience method to partition existing recordsets without the overhead of a
read_group()
, but performs no aggregation.Примітка
unlike
itertools.groupby()
, does not care about input ordering, however the tradeoff is that it can not be lazy
Спадкування та розширення¶
Odoo надає три різні механізми для модульного розширення моделей:
створення нової моделі з існуючої, додаючи нову інформацію до копії, але залишаючи оригінальний модуль як є
розширення моделей, визначених в інших модулях на місці, замінюючи попередню версію
делегування деяких полів моделі записам, які вона містить

Класична спадковість¶
При спільному використанні атрибутів _inherit
і _name
Odoo створює нову модель, використовуючи існуючу (надається через _inherit
) як основу. Нова модель отримує всі поля, методи та метаінформацію (за замовчуванням та інше) зі своєї бази.
class Inheritance0(models.Model):
_name = 'inheritance.0'
_description = 'Inheritance Zero'
name = fields.Char()
def call(self):
return self.check("model 0")
def check(self, s):
return "This is {} record {}".format(s, self.name)
class Inheritance1(models.Model):
_name = 'inheritance.1'
_inherit = 'inheritance.0'
_description = 'Inheritance One'
def call(self):
return self.check("model 1")
та їх використання:
a = env['inheritance.0'].create({'name': 'A'})
b = env['inheritance.1'].create({'name': 'B'})
a.call()
b.call()
дасть:
«Це запис моделі 0 A» «Це запис моделі 1 B»
друга модель успадкувала від першої моделі метод check
і його поле name
, але перевизначила метод call
, як при використанні стандартного успадкування Python.
Розширення¶
Якщо використовувати _inherit
, але не використовувати _name
, нова модель замінить існуючу, по суті розширюючи її на місці. Це корисно для додавання нових полів чи методів до існуючих моделей (створених в інших модулях), або для їх налаштування чи переналаштування (наприклад, для зміни порядку сортування за замовчуванням):
class Extension0(models.Model):
_name = 'extension.0'
_description = 'Extension zero'
name = fields.Char(default="A")
class Extension1(models.Model):
_inherit = 'extension.0'
description = fields.Char(default="Extended")
record = env['extension.0'].create({})
record.read()[0]
дасть:
{'name': "A", 'description': "Extended"}
Примітка
Це також призведе до появи різних автоматичних полів, якщо вони не були вимкнені
Делегування¶
Третій механізм успадкування забезпечує більшу гнучкість (його можна змінити під час виконання), але меншу потужність: за допомогою _inherits
модель делегує пошук будь-якого поля, не знайденого в поточній моделі до «дитячих» моделей. Делегування виконується за допомогою полів Reference
, автоматично встановлених у батьківській моделі.
Основна відмінність полягає в значенні. Під час використання делегування модель має один замість є один, перетворюючи зв’язок у композицію замість успадкування:
class Screen(models.Model):
_name = 'delegation.screen'
_description = 'Screen'
size = fields.Float(string='Screen Size in inches')
class Keyboard(models.Model):
_name = 'delegation.keyboard'
_description = 'Keyboard'
layout = fields.Char(string='Layout')
class Laptop(models.Model):
_name = 'delegation.laptop'
_description = 'Laptop'
_inherits = {
'delegation.screen': 'screen_id',
'delegation.keyboard': 'keyboard_id',
}
name = fields.Char(string='Name')
maker = fields.Char(string='Maker')
# a Laptop has a screen
screen_id = fields.Many2one('delegation.screen', required=True, ondelete="cascade")
# a Laptop has a keyboard
keyboard_id = fields.Many2one('delegation.keyboard', required=True, ondelete="cascade")
record = env['delegation.laptop'].create({
'screen_id': env['delegation.screen'].create({'size': 13.0}).id,
'keyboard_id': env['delegation.keyboard'].create({'layout': 'QWERTY'}).id,
})
record.size
record.layout
призведе до:
13.0
'QWERTY'
і можна писати безпосередньо в делегованому полі:
record.write({'size': 14.0})
Попередження
при використанні успадкування делегування методи не успадковуються, лише поля
Попередження
_inherits
більш-менш реалізовано, уникайте його, якщо можете;chained
_inherits
по суті не реалізовано, ми не можемо нічого гарантувати щодо остаточної поведінки.
Інкрементне визначення полів¶
Поле визначається як атрибут класу в класі моделі. Якщо модель розширено, можна також розширити визначення поля, перевизначивши поле з тим же ім’ям і тим самим типом у підкласі. У цьому випадку атрибути поля беруться з батьківського класу та замінюються тими, що надані в підкласах.
Наприклад, другий клас нижче лише додає підказку до поля state
:
class First(models.Model):
_name = 'foo'
state = fields.Selection([...], required=True)
class Second(models.Model):
_inherit = 'foo'
state = fields.Selection(help="Blah blah blah")
Управління помилками¶
Модуль винятків Odoo визначає кілька основних типів винятків.
Ці типи розуміються рівнем RPC. Будь-який інший тип винятку, що з’являється до рівня RPC, розглядатиметься як „Помилка сервера“.
Примітка
Якщо ви плануєте запровадити нові винятки, перегляньте модуль odoo.addons.test_exceptions
.
- exception odoo.exceptions.UserError(message)[source]¶
Загальна помилка, керована клієнтом.
Зазвичай, коли користувач намагається зробити щось, що не має сенсу з огляду на поточний стан запису. Семантично можна порівняти з загальними 400 кодами стану HTTP.
- exception odoo.exceptions.RedirectWarning(message, action, button_text, additional_context=None)[source]¶
Попередження з можливістю перенаправлення користувача замість простого відображення попередження.
- Параметри
message (str) – повідомлення про винятки та зовнішній модальний вміст
action_id (int) – id дії, куди потрібно виконати переспрямування
button_text (str) – текст, який потрібно розмістити на кнопці, яка ініціює переспрямування.
additional_context (dict) – параметр, переданий у action_id. Можна використовувати для обмеження перегляду, наприклад, до active_ids.
- exception odoo.exceptions.AccessDenied(message='Access Denied')[source]¶
Помилка логіна/паролю.
Примітка
Немає відстеження.
Приклад
Коли ви намагаєтесь увійти з неправильним паролем.
- exception odoo.exceptions.AccessError(message)[source]¶
Помилка прав доступу.
Приклад
Коли ви намагаєтесь прочитати запис, що вам заборонено.
- exception odoo.exceptions.CacheMiss(record, field)[source]¶
У кеші відсутні значення.
Приклад
Коли ви намагаєтеся прочитати значення в очищеному кеші.