Срез исходников: ветка
19.0, commit7d3a7fac27332f440404383f41cc113bf3face53от 14 марта 2026 года.
Первоначальная область анализа: Python-пакетodoo/безodoo/addons/**;odoo/addons/baseрассматривается только как опорный системный модуль. После уточнения область дополнена отдельным полным аудитом обязательного модуля/Users/max/Dev/odoo_19/addons/web; см. раздел 27 и связанный подробный отчёт.
В ядре вне odoo/addons/** находится 207 файлов и 63 542 строки:
| Область | Файлов | Строк | Назначение |
|---|---|---|---|
корень odoo/ |
9 | 4 762 | bootstrap, HTTP, БД, исключения, логирование |
_monkeypatches/ |
20 | 2 620 | ранняя нормализация Python и сторонних библиотек |
api/, fields/, models/ |
3 | 74 | стабильные публичные фасады |
cli/ с шаблонами |
46 | 2 862 | команды, конфигурация запуска и scaffold |
modules/ |
8 | 2 091 | обнаружение, граф, загрузка и миграции модулей |
orm/ |
23 | 20 018 | модельная система, поля, домены, registry, cache |
osv/ |
2 | 477 | совместимость старого API выражений |
service/ |
6 | 2 540 | RPC, серверные процессы, DB administration |
tests/ |
13 | 5 342 | собственный test runtime Odoo |
tools/ |
67 | 20 730 | SQL/XML/i18n/media/security/diagnostics utilities |
upgrade/, upgrade_code/ |
10 | 2 027 | namespace миграций и переписывание кода addons |
Каждый из этих файлов включён в инвентаризацию; Python-файлы разобраны по определениям и импортам, а ключевые исполняемые пути — по телам функций и точкам вызова. Приложение в конце даёт назначение каждого файла, чтобы редкая утилита или compatibility shim не потерялись за крупными слоями.
Не включены:
- соседний каталог репозитория
addons/; - подробная бизнес-логика
res.partner, валют, стран, компаний и других моделейbase; - web-клиент в первоначальном основном проходе (теперь он разобран в разделе 27 и отдельном отчёте) и прикладные addons;
- Enterprise-код, инфраструктура сборки и внешние сервисы, которых нет в данном дереве.
Odoo — не просто MVC-приложение и не просто ORM. Ядро одновременно выполняет три роли:
- Компилятор расширяемой модели. Оно читает manifests, строит граф addons, импортирует Python-классы-определения, сливает
_inherit/_inherits, строит новый набор классов для конкретной базы и синхронизирует его с PostgreSQL. - Транзакционный runtime. Оно представляет данные recordset-объектами, связывает их с
Environmentи курсором, ведёт field cache, prefetch, dirty values, dependency graph вычисляемых полей и превращает домены в SQL. - Сервер приложений. Оно выбирает базу, восстанавливает сессию, аутентифицирует запрос, сопоставляет маршрут, вызывает ORM/RPC, повторяет конфликтующие транзакции, коммитит изменения и синхронизирует кэши между процессами.
Ключевая единица изоляции — база данных. Для каждой базы существует собственный Registry; в каждом процессе registry содержит собственные динамические классы моделей и кэши. Консистентность между процессами обеспечивается PostgreSQL-сигналами, а не общей памятью.
flowchart TB
P["Процесс Odoo"] --> C["CLI + config"]
P --> S["HTTP/cron server"]
S --> R1["Registry базы A"]
S --> R2["Registry базы B"]
R1 --> M1["Динамические классы моделей A"]
R2 --> M2["Динамические классы моделей B"]
M1 --> E1["Environment + Transaction + Cursor"]
M2 --> E2["Environment + Transaction + Cursor"]
E1 --> DB1[("PostgreSQL A")]
E2 --> DB2[("PostgreSQL B")]
DB1 -. "registry/cache signaling" .-> R1
DB2 -. "registry/cache signaling" .-> R2
Номера ниже обозначают порядок понимания, а не строгую однонаправленную зависимость: tools и base пересекают несколько уровней.
| № | Логический слой | Главные файлы | Что он гарантирует |
|---|---|---|---|
| 0 | Runtime prerequisites | release.py, init.py, _monkeypatches/* |
допустимые версии, UTC, GC, gevent mode, совместимость зависимостей |
| 1 | Публичный Python API | api/, fields/, models/, orm/__init__.py, exceptions.py |
стабильные точки импорта и общая таксономия ошибок |
| 2 | Конфигурация и команды | tools/config.py, cli/*, odoo-bin, __main__.py |
слияние настроек и выбор режима процесса |
| 3 | Addon namespace и metadata | modules/module.py |
пути addons, manifests, Python/external dependencies, импорт пакетов |
| 4 | Граф и lifecycle модулей | module_graph.py, loading.py, migration.py, db.py |
порядок base/install/upgrade/uninstall, data files, hooks, schema |
| 5 | Registry и сборка классов | orm/registry.py, model_classes.py, models.py |
отдельная модельная вселенная на базу, _inherit/_inherits, setup |
| 6 | Recordset и Environment | orm/models.py, environments.py, identifiers.py |
API работы с наборами записей и транзакционным состоянием |
| 7 | Система полей | orm/fields*.py, commands.py, decorators.py |
descriptors, conversion, relational commands, compute/inverse/related |
| 8 | Query language | orm/domains.py, tools/query.py, tools/sql.py, osv/expression.py |
Domain AST → оптимизированный Query → параметризованный SQL |
| 9 | Persistence и транзакции | sql_db.py, CRUD в orm/models.py |
pool, cursor, savepoint, flush, commit/rollback callbacks, DB I/O |
| 10 | Кэш и реактивные зависимости | environments.py, registry.py, tools/cache.py |
field cache, dirty state, recompute graph, method cache, invalidation |
| 11 | Security и tenancy | ORM access checks + модели base |
ACL, record rules, field groups, company consistency, auth/session |
| 12 | HTTP и RPC | http.py, service/model.py, service/common.py, service/security.py |
маршрутизация, dispatchers, CSRF, session, RPC boundary, retrying |
| 13 | Процессы и фоновые задачи | service/server.py, netsvc.py, base.ir_cron |
threaded/prefork/gevent, workers, cron, reload, limits, logging |
| 14 | Данные и эволюция | tools/convert.py, import_xml.rng, migration.py, upgrade_code/* |
XML/CSV/SQL import, migrations данных, source-to-source upgrade |
| 15 | Системный control plane | odoo/addons/base поверхностно |
metadata базы, access, views/actions, cron, attachments, users/companies |
| 16 | Cross-cutting tools | tools/* |
i18n, XML, media, mail, PDF, safe eval, profiler, collections |
| 17 | Тестовый runtime | tests/* |
транзакционная изоляция, tags, HTTP/Chrome tests, reporting |
odoo-bin импортирует odoo.cli и вызывает odoo.cli.main(). Запуск python -m odoo приходит в odoo/__main__.py и заканчивается тем же dispatcher команд.
init.py исполняется до тяжёлых импортов:
- проверяет границы Python, заданные в
release.py— для этого среза 3.10–3.13; - меняет пороги cyclic GC и включает измерение времени GC;
- вызывает
_monkeypatches.patch_init()до импорта затрагиваемых библиотек; - экспортирует
SUPERUSER_ID,_,_ltиCommandв верхнее пространствоodoo.
Monkeypatch-слой построен аккуратнее, чем серия немедленных импортов. _monkeypatches/__init__.py вставляет finder первым в sys.meta_path. Если целевая библиотека уже загружена, её patch_module() вызывается сразу; если нет — loader оборачивается, и patch применяется сразу после будущего импорта.
Что нормализуется:
site.py:TZ=UTC, alias кодировок; специальная командаgeventвыполняетgevent.monkey.patch_all(), включает psycopg2 wait callback и ставитodoo.evented=True;werkzeug,urllib3,email,mimetypes: HTTP, headers, MIME и совместимость версий;lxml,bs4,docutils,zeep: XML/HTML/SOAP поведение и безопасность;csv,locale,pytz,num2words,stdnum: единообразие данных и локалей;xlrd,xlwt,xlsxwriter: поведение spreadsheet-библиотек;ast,re: совместимость Python runtime.
Это нижний compatibility substrate. Ошибка здесь влияет на все базы и все addons ещё до создания конфигурации или registry.
odoo.api, odoo.fields и odoo.models — фасады над odoo.orm, а не отдельные реализации. Они позволяют addon-коду зависеть от стабильных путей импорта, пока внутренние файлы ORM можно перестраивать. odoo.modules.registry аналогично оставляет compatibility import нового odoo.orm.registry.
orm/__init__.py документирует и соблюдает порядок импортов ORM. Порядок критичен из-за metaclass-регистрации, descriptors и циклических типов.
exceptions.py задаёт ошибки, пересекающие Python, HTTP и RPC:
UserError,ValidationError,RedirectWarning— ожидаемые пользовательские исходы;AccessDenied— ошибка аутентификации, HTTP 403, без полезного traceback наружу;AccessError— нарушение прав, HTTP 403;MissingError— исчезнувшая/недоступная запись, HTTP 404;LockError— конфликт блокировки, HTTP 409;ConcurrencyError— внутренний сигнал об обязательном повторе транзакции;CacheMiss— внутреннее отсутствие значения field cache, не бизнес-ошибка.
Важно: HTTP status — лишь одна проекция исключения. JSON-RPC может завернуть то же исключение в собственный envelope, а service.model.retrying() может вообще не вернуть его клиенту, а откатить и повторить транзакцию.
tools/config.py — типизированный registry опций и одновременно ChainMap со следующим приоритетом:
runtime overrides > command line > environment > config file > defaults
У каждой опции описано, разрешена ли она в CLI, environment и файле, как она парсится и экспортируется. Post-processing:
- проверяет
addons_path,upgrade_path, DB и worker settings; - сводит
--init,--update,--reinitв maps состояний модулей; - выводит
test_enableиз tags и включаетstop_after_initдля тестов; - разворачивает
--dev=allи logger overrides; - настраивает отдельные primary/readonly PostgreSQL endpoints и pools;
- вычисляет
data_dir,session_dir,filestore, базовые пути addons; - хеширует и проверяет master password администратора БД через
crypt_context.
Три стандартных addon-корня имеют разную роль:
odoo/addons— встроенныйbase;${data_dir}/addons/<series>— addons из data directory;- явно переданные пути и соседний
<repo>/addons— прикладные модули.
cli/command.py автоматически регистрирует subclasses Command. Встроенные команды импортируются лениво. CLI addons могут быть найдены по путям и загружены точечно, без импорта всего addon. До выбора команды pre-parser понимает лишь общий --addons-path.
Если команда не указана, выбирается server; help показывает registry команд. Основные режимы:
server— полноценный запуск, создание отсутствующей явно запрошенной БД и initbase;start— developer shortcut: обнаруживает каталог addons, имя БД и запускает server;shell— интерактивный Python с registry/environment;db— create/drop/dump/restore/duplicate/rename и другие DB operations;module,i18n,neutralize,populate,cloc— обслуживание addon metadata и данных;scaffold— генерация default/theme/payroll skeletons;deploy— упаковка addon и upload на удалённый Odoo endpoint;upgrade_code— последовательные source transforms;obfuscate— подготовка обезличенного dump;help— справка.
cli/server.py проверяет опасный запуск от root/PostgreSQL system user, настраивает logging/PID, а затем передаёт управление service.server.start().
modules/module.py создаёт namespace odoo.addons из упорядоченных addon paths. После инициализации namespace finders замораживаются, чтобы один и тот же module name не начал резолвиться иначе посреди процесса.
Manifest:
- ищет
__manifest__.py/legacy manifest names; - читает Python literal через
ast.literal_eval, а не исполняет произвольный manifest; - добавляет defaults, валидирует обязательные поля и нормализует version;
- перечисляет dependencies, data/demo, assets, hooks, external Python/binary dependencies;
- даёт пути static/icon и определяет installability.
Импорт addon вызывает его __init__.py, регистрируя definition classes через MetaModel; post_load — процессный hook, который может исполняться до registry и поэтому должен быть database-agnostic.
modules/db.py создаёт минимальную базу из base/data/base_data.sql сырым SQL, прежде чем ORM этой базы вообще существует. Затем manifests отражаются в ir_module_module, создаются зависимости/categories/XML IDs и рекурсивно отмечаются auto-install modules. Здесь же проверяются возможности PostgreSQL вроде unaccent/trigram.
Следствие: чистое Python-ядро импортируется без base, но рабочая Odoo database не существует без bootstrap-данных base.
module_graph.py строит DAG из manifests и DB state. ModuleNode несёт versions, state, dependency depth, load phase и стабильное order_name; итоговая сортировка использует (phase, depth, order_name).
Граф:
- отделяет
base, обычную загрузку, install и upgrade phases; - отбрасывает отсутствующие, uninstallable и неподходящие по состоянию узлы;
- обнаруживает cycles;
- распространяет невозможность загрузки на dependents;
- позволяет пройти граф в обратном порядке при uninstall.
modules/loading.py — центральный build pipeline:
- Base phase. Инициализация путей/БД; определение уже переведённых и company-dependent колонок; импорт и setup
base. - State phase. Обновление списка manifests; перевод выбранных модулей в
to install,to upgrade,to removeили reinit. - Graph loop. Повторное расширение графа, потому что hooks во время загрузки могут изменить состояния других модулей.
- На каждый module node: pre-migrations → Python import → добавление definition classes в registry → incremental setup/schema → data/demo → post-migrations → translations →
at_installtests → module state/version → flush и commit. - End migrations и проверки: constraints, cleanup metadata, удалённые columns/XML IDs/cron residue, проверка extended/custom fields, schema и views.
- Uninstall. Reverse dependency order, uninstall hooks и data removal; затем рекурсивная полная пересборка registry.
- Finalization. register hooks, окончательные constraints/not-null,
post_installtests на уровне preload.
Коммит после успешно загруженного модуля делает installation pipeline возобновляемым по модулям, но означает, что это не одна общая ACID-транзакция на весь upgrade набора.
flowchart LR
A["Manifest paths"] --> B["ModuleGraph"]
B --> C["pre migration"]
C --> D["import Python definitions"]
D --> E["registry.load"]
E --> F["model/field setup + schema"]
F --> G["XML/CSV/SQL + demo"]
G --> H["post migration + translations"]
H --> I["at_install tests"]
I --> J["flush + commit module"]
J --> K["constraints/cleanup/hooks"]
modules/migration.py ищет versioned pre-, post- и end- scripts в addon migrations/, upgrades/ и namespace odoo.upgrade; сравнение версий определяет, какие scripts лежат между установленной и целевой версиями. Они импортируются изолированно и получают cursor/version.
orm/registry.py — mapping model_name → model class для одной базы. Это не просто cache схемы.
Registry.new(dbname, update_module=...):
- берёт глобальную блокировку построения;
- создаёт и временно публикует registry, чтобы рекурсивный код уже мог его найти;
- подключает DB handles и inter-process signaling;
- проверяет marker незавершённого module update;
- вызывает
load_modules(); - завершает setup, помечает
loaded/readyи рассылает changes; - при ошибке удаляет неполный registry и освобождает его ресурсы.
Глобальный LRU registries ограничен памятью: оценка — около 15 MB на одну базу, либо фиксированный fallback. Eviction закрывает DB pools/ресурсы registry данного процесса, но не меняет БД.
orm/models.py использует MetaModel для сбора классов, импортированных из odoo.addons.<module>. Такой класс описывает вклад addon, но ещё не является итоговой моделью конкретной базы.
orm/model_classes.py строит отдельный dynamic class внутри Registry:
_nameзадаёт модель;_inherit='x'без нового_nameрасширяетxin-place в registry;_name='y', _inherit='x'создаёт классическое наследование модели;_inherits={'x': 'x_id'}делегирует хранение черезMany2oneи создаёт inherited related fields;- кроме модели
base, все модели неявно наследуютbase; - MRO и
__bases__вычисляются из установленных именно в этой БД addons.
Поэтому один Python-процесс может обслуживать две базы, где одно имя модели имеет различный набор полей и методов.
Сборка идёт фазами:
- определить итоговый MRO;
- собрать и слить definitions полей;
- добавить manual models/fields из DB metadata;
- настроить attributes полей, related chains и comodels;
- построить compute dependencies, inverse maps, relation metadata и trigger trees;
- создать/изменить schema и reflection rows;
- зарегистрировать constraints, indexes, hooks и post-setup state.
Fields могут переиспользоваться между registries, если они неизменны; overridden, related и registry-specific fields клонируются. Это компромисс между memory footprint и строгой изоляцией.
AbstractModel:_auto=False,_abstract=True; mixin без собственной обычной таблицы.Model:_auto=True,_abstract=False; постоянные записи.TransientModel: временные записи, creator-oriented access и autovacuum по возрасту/количеству с минимальным окном около пяти минут.
Экземпляр BaseModel имеет __slots__ = (env, _ids, _prefetch_ids). Отдельного класса «одна запись» нет:
- пустой recordset — model proxy;
- recordset из одного id — singleton;
- несколько ids — упорядоченная коллекция;
- iteration создаёт singleton recordsets, сохраняющие общий prefetch set;
|,&,-, membership и sequence operations работают над ids;filtered,mapped,grouped,sortedсохраняют декларативный стиль.
NewId представляет псевдозапись до INSERT или onchange-копию. Он ложен в boolean context, может ссылаться на origin или ref и живёт только в памяти транзакции.
orm/environments.py связывает:
cursor + uid + immutable context + su flag + registry
Environments дедуплицируются внутри одной transaction. env['res.users'] берёт итоговый class из registry и возвращает пустой recordset, связанный с этим env.
Ленивые properties дают user, company, companies, lang, tz. Производные environments создаются через:
with_context— новый immutable context;with_user— другой uid;sudo— переключает bypass access checks, но сам по себе не обязан менять uid;with_company— корректирует company context.
Это принципиальное различие: identity пользователя, superuser mode и allowed companies — три независимые координаты.
Transaction прикрепляется к cursor лениво и обща для всех environments на нём. Она содержит:
- WeakSet environments и default environment;
- field cache и dirty record ids;
- patches для x2many;
- protected fields во время inverse/compute;
- очередь
tocompute; - compatibility cache;
- временные файловые пути и cleanup state.
Environment — view на transaction с конкретными security/context coordinates; он не равен транзакции. Создание нового env на том же cursor не создаёт новый commit boundary.
Base Field в orm/fields.py одновременно является:
- Python descriptor;
- типом конвертации cache/record/write/column/export;
- декларацией SQL column или relation;
- узлом compute dependency graph;
- носителем ACL groups, default, index, copy и prefetch policy.
Упрощённый Field.__get__:
- проверить field-level groups и singleton;
- если stored compute ожидает recompute — выполнить его;
- попытаться взять значение из field cache;
- для real stored record запустить batch prefetch из БД;
- для
NewIdпопробовать origin/default; - вычислить non-stored compute;
- для delegated field пройти через
_inheritsparent; - вернуть типизированное cache value или поднять
MissingErrorдля исчезнувшей реальной записи.
Для реальной записи assignment обычно вызывает write(). Для protected/new records descriptor меняет cache, inverse state и dependency notifications без немедленного SQL. Это позволяет onchange, inverse methods и графу recompute работать с виртуальным состоянием.
fields_misc.py:Boolean,Json,Idи общие простые значения;fields_numeric.py:Integer,Float,Monetary, rounding/digits;fields_textual.py:Char,Text,Html; переводы могут храниться в JSONB,LangProxyDictдаёт language view;fields_temporal.py:Date,Datetime, timezone/context helpers;fields_binary.py:Binary,Image, attachment-backed storage и image processing;fields_selection.py: статические/динамические selections и extension;fields_reference.py: polymorphicReference,Many2oneReference;fields_relational.py:Many2one,One2many,Many2many, inverse/relation tables/ondelete;fields_properties.py: динамические JSONB properties и их schema definitions.
orm/commands.py делает явным старый tuple-протокол:
| Код | API | Смысл |
|---|---|---|
| 0 | Command.create(values) |
создать связанную запись |
| 1 | Command.update(id, values) |
изменить связанную запись |
| 2 | Command.delete(id) |
удалить запись из БД |
| 3 | Command.unlink(id) |
разорвать связь |
| 4 | Command.link(id) |
добавить существующую связь |
| 5 | Command.clear() |
очистить связи |
| 6 | Command.set(ids) |
заменить множество связей |
orm/decorators.py не просто оборачивает вызовы. @depends, @depends_context, @constrains, @onchange, @ondelete, @autovacuum, @model, @model_create_multi, @private, @readonly оставляют metadata, которое registry собирает во время setup. @private вместе с именами _... закрывает метод для RPC.
orm/domains.py в Odoo 19 представляет domain как immutable AST: boolean constants, Not, And, Or, Condition, Custom. Legacy prefix lists принимаются и преобразуются.
Поддерживаются comparisons, in, like-семейство, hierarchy operators и relation quantifiers any, not any, any!. any! намеренно обходит record rules comodel и потому должен применяться как привилегированный инструмент, а не как косметический вариант any.
Оптимизация выполняется до fixed point с защитным пределом:
- flatten/constant folding и boolean algebra;
- переписывание custom field search methods;
- нормализация типов и ложных/null значений;
- преобразование relational/hierarchy predicates;
- присоединение access-rule domains на нужных моделях;
- генерация SQL fragments и joins.
osv/expression.py сохраняет старый namespace и compatibility helpers, но новая семантическая модель находится в orm.domains.
tools/query.py хранит FROM, joins, where, order, limit/offset и может memoize полученные ids. Это промежуточное представление, позволяющее полям и security rules добавлять joins/conditions без ручной конкатенации строки.
tools/sql.py задаёт composable SQL(code, params, to_flush=...):
- code и parameters хранятся раздельно;
- identifiers quote-ятся явно;
- nested SQL objects безопасно композиционируются;
to_flushнесёт metadata полей, которые нужно flush до выполнения запроса;- рядом находятся helpers introspection/DDL: tables, columns, indexes, constraints, FKs.
Это не sandbox для произвольного SQL, но основной способ не смешивать data с query text и связать raw SQL с pending ORM writes.
sql_db.py содержит два глобальных pools — read/write и readonly. ConnectionPool:
- ограничивает число соединений;
- переиспользует подходящее idle connection по DSN;
- сбрасывает state перед выдачей;
- обнаруживает leaked/closed connections;
- никогда не включает пароль в сравнение или diagnostic output.
Connection — настроенный handle; cursor() создаёт Odoo Cursor над psycopg2. Cursor работает в repeatable-read, на replica включает readonly session, считает queries/time и вызывает profiling hooks.
BaseCursor имеет callback queues:
precommit → PostgreSQL COMMIT → clear transaction → postcommit
prerollback → PostgreSQL ROLLBACK → postrollback
Перед commit flush() повторяет transaction flush и precommit hooks до стабилизации, максимум десять циклов. Context manager коммитит только при отсутствии исключения и всегда закрывает cursor.
Savepoint бывает обычным и flushing. Flushing savepoint делает flush до/после, а при ошибке очищает transaction cache до rollback-to-savepoint, чтобы Python state не утверждал, будто откатившиеся данные существуют.
Критическое различие:
flushсинхронизирует pending ORM state с текущей DB transaction;commitделает transaction видимой другим connections;invalidateзабывает cache values;clear/resetсбрасывает Python-side transaction state;- ни одна из этих операций не является синонимом другой.
Field cache индексируется примерно так:
field → context-cache-key → record id → cache value
Context key включает только объявленные зависимости и специальные координаты: company, uid+su, lang, active_test, bin_size. Если compute зависит от произвольного context key, но не объявляет его через depends_context, значение может быть переиспользовано неверно.
Dirty field означает: cache уже содержит новое значение, а PostgreSQL ещё нет. Invalidation записи удаляет все context variants соответствующего поля/id.
Recordset хранит собственные ids и более широкий _prefetch_ids. Чтение одного stored field у singleton обычно загружает это поле сразу для всего prefetch batch. Related traversal распространяет prefetch на comodel. Поэтому idiomatic iteration не создаёт N+1 автоматически, но slicing, новые независимые browse-наборы и raw SQL могут разрушить batching.
Во время registry setup зависимости полей превращаются в TriggerTree. modified(..., before=True/after=True) проходит inverse relations:
- stored computes ставятся в
tocompute; - non-stored cached values инвалидируются;
- relation change отмечается до и после, чтобы затронуть и старых, и новых owners.
Environment.flush_all() выполняет fixed-point loop: recompute → dirty writes → новые dependency notifications, пока состояние не стабилизируется; защитный предел ловит циклы/неустойчивые computes.
tools/cache.py реализует ormcache families. Это registry-level LRU для результатов методов и не то же самое, что transaction field cache.
Registry ведёт именованные caches и sequences. Межпроцессная синхронизация использует append-only PostgreSQL signaling tables:
- registry sequence mismatch требует перезагрузить структуру registry;
- cache sequence mismatch очищает конкретный named cache;
signal_changes()публикует локальные invalidations после успешной transaction;check_signaling()применяется на границе новых запросов/работ.
Порядок источников default values:
context['default_<field>'];ir.defaultбез company override;- callable/static
field.default; - company-dependent fallback;
- defaults delegated parents для
_inherits.
search:
- нормализует/оптимизирует Domain;
- добавляет
active_test, если применимо; - проверяет model ACL;
- добавляет record-rule domain;
- строит Query/joins/order/limit;
- выполняет параметризованный SQL и создаёт recordset.
fetch проверяет field ACL, расширяет stored dependency fields, группирует prefetch и кладёт значения БД в cache, не перетирая dirty values текущей transaction.
Упрощённая последовательность:
- model create ACL и field groups;
- defaults и log-access metadata;
- precomputed fields;
- create/update delegated
_inheritsparents; - batch INSERT column fields;
- cache, inverse и dependency notifications;
- relational/non-column fields;
- recompute, Python/SQL constraints и company checks;
- optional XML ID при import mode.
write проверяет ACL/rules/field groups, нормализует значения, защищает inverse/compute fields, делает before-notification relation dependencies, применяет column и relational updates в определённом приоритете, вызывает inverses, затем after-notification, constraints и company checks. Dirty scalar values могут быть объединены в batch UPDATE только при flush.
unlink выполняет ACL и @ondelete hooks, flush необходимых данных, dependency notifications, chunked SQL DELETE, очистку XML IDs/attachments/defaults и company-dependent JSONB references, после чего инвалидирует затронутый cache. SQL FK policies остаются последней линией referential integrity.
_auto_init/registry initialization сравнивают declarative model с PostgreSQL: создают tables/columns, конвертируют types, назначают recompute новых stored fields, применяют indexes/constraints/FKs и отражают результат в ir.model*. Это объясняет, почему module installation одновременно является import Python, migration engine и schema compiler.
Security распределена между Python kernel и системными моделями base.
- Authentication — кто пользователь/session/API key; HTTP и
res.users. - Model ACL — разрешены ли create/read/write/unlink для модели;
ir.model.access. - Record rules — разрешены ли конкретные rows;
ir.ruleдобавляет domain по operation. - Field groups — можно ли видеть/писать конкретное поле; metadata
Field.groups.
Отдельно ORM проверяет multi-company consistency отношений. sudo() обходит access controls, но не должно восприниматься как автоматическая смена пользователя или разрешённых компаний.
ACL проверяется до row query; record rules становятся частью SQL/domain либо проверяются на recordset; field access применяется при fetch/read/write и descriptor access. Ошибка доступа может намеренно выглядеть как отсутствие записи, чтобы не раскрывать существование объекта.
service/model.py отклоняет:
- имена методов, начинающиеся с
_; - unsafe/static/class attributes;
- methods с
@api.private.
call_kw определяет model-method или record-method форму, восстанавливает recordset по ids, применяет context и адаптирует специальные create conventions. Это boundary adapter; сами ACL всё равно обязаны применяться ORM/model code.
http.py — transport kernel. DB-aware policy делегируется модели ir.http из base.
Controller classes также расширяются addons. При генерации routing rules Odoo перестраивает controller inheritance только из установленных модулей и сливает metadata нескольких @route по MRO.
Route metadata включает:
type:http,jsonrpc,json2;auth:none,public,user,bearer;- methods, CORS, CSRF, captcha;
- readonly policy;
- session saving и response behavior.
auth='none' — единственный режим, пригодный без registry/DB. Bearer по умолчанию stateless. Controller method вызывает model/API code уже внутри request environment.
Request пытается установить DB из валидной session, stateless header X-Odoo-Database, dbfilter или monodb. Нельзя одновременно принять cookie-session одной базы и противоречащий ей header другой. Database selection происходит до построения registry/env и является security boundary multi-tenancy.
sequenceDiagram
participant W as WSGI server
participant A as Application
participant H as http.py Request
participant R as Registry
participant IH as base.ir_http
participant M as Controller/Model
participant DB as PostgreSQL
W->>A: WSGI environ
A->>H: sanitize proxy + create request
alt static asset
H-->>A: file response
else no database
H->>M: auth=none route
else database request
H->>R: acquire/check signaling
R->>DB: readonly or primary cursor
H->>IH: _match + _authenticate
IH->>M: dispatch endpoint
M->>DB: ORM/SQL
H->>DB: flush + commit
H->>R: signal cache changes
end
A-->>W: response / mapped exception
DB request сначала получает Registry, открывает cursor, строит Environment и вызывает ir.http._match. Readonly route может пойти на replica. Если внутри обнаружилась запись, request повторяется с primary read/write cursor.
Весь endpoint исполняется через service.model.retrying(). На lock/serialization/deadlock/ConcurrencyError текущая transaction откатывается, env/registry/session/file streams сбрасываются и вызов повторяется до пяти раз с exponential random backoff. Следствие для addon-кода: до commit внешние side effects должны быть идемпотентными или отложенными в postcommit.
HttpDispatcher: объединяет path/query/form/files, валидирует CSRF для unsafe methods, вызывает endpoint и строит обычный Response.JsonRPCDispatcher: принимает JSON-RPC named params; полеmethodenvelope исторически не является именем model method; возвращает JSON-RPC result/error envelope.Json2Dispatcher: JSON body/args, более естественные HTTP statuses и сериализация ошибок.
Общий dispatcher pre/post слой обрабатывает content limits, preflight/CORS, сохранение/ротацию session, future response headers и CSP.
Filesystem session store разбивает файлы по префиксу SID и валидирует ключи. Session — JSON-normalized mutable mapping с flags dirty, new, rotate. Session token связывает user identity и SID; rotation/delete имеют grace semantics.
CSRF token — HMAC от DB secret, первых байтов SID и expiry. Проверка связана не только с cookie, но и с конкретной базой и сроком жизни. Аутентификация поддерживает partial pre-session и финализацию MFA; device metadata попадает в audit trail через security service/base models.
service/common.py: version и legacy authentication RPC;service/db.py: master-password-guarded управление databases и filestore, pg_dump/restore, DB listing/filtering;service/security.py: session token verification/migration и device logging;http.dispatch_rpc: router старыхcommon,db,objectservice namespaces.
service/server.py выбирает topology:
odoo.evented == True → GeventServer
workers > 0 → PreforkServer
иначе → ThreadedServer
Перед стартом загружаются server-wide modules — process-level packages, по умолчанию base и web. Затем preload_registries() может собрать registries указанных БД, выполнить updates/tests и только после этого принимать traffic.
- Werkzeug HTTP server с thread-per-request;
- daemon cron threads в том же процессе;
- signal handling и graceful shutdown;
- request, CPU/real time и memory limits;
- watcher
.pyфайлов с compile-before-reload; - optional systemd socket activation.
Master process:
- владеет listening socket;
- fork-ит
WorkerHTTPиWorkerCron; - отдельно запускает gevent long-polling/websocket process;
- поддерживает требуемое количество поколений workers;
- получает heartbeats/watchdog data через pipes;
- убивает workers по memory/time/request limits;
- делает graceful/hot reload, сохраняя socket.
Каждый worker имеет собственные Python heaps, registries и caches; общая координация проходит через PostgreSQL и OS primitives.
Gevent WSGI server получает monkeypatched stdlib ещё в site patch, кладёт raw socket в WSGI environ для websocket upgrade и использует собственный watchdog/memory accounting. Это cooperative concurrency; блокирующая библиотека без gevent integration блокирует event loop.
Worker/listener использует PostgreSQL LISTEN cron_trigger плюс periodic polling с jitter. Для найденной базы вызывается ir.cron._process_jobs. Cron model захватывает jobs, ведёт progress/failure state и исполняет actions в отдельных transaction boundaries. Это base control plane, а не scheduler внутри orm.
netsvc.py добавляет Odoo log levels/formatters, colored output, DB log handler, query/elapsed performance annotations и warning stacks. loglevels.py регистрирует специальные numeric levels, включая runbot-oriented output.
tools/convert.py загружает module data:
- XML валидируется
import_xml.rng; - CSV идёт через
Model.loadи типовые converters; - SQL исполняется напрямую в module transaction;
- JS data files игнорируются Python loader и предназначены другим consumers.
XML dialect включает record, delete, function, menuitem, template, asset, ref, eval, search, nested records, sequence. Loader ведёт XML IDs, noupdate, forcecreate, import context и связывает records между модулями.
Versioned migration scripts — часть module loader и исполняются внутри upgrade lifecycle до/после module data, а end scripts — после основного графа. neutralize.sql установленного набора модулей агрегируется modules/neutralize.py для обезвреживания production side effects в копии БД.
cli/upgrade_code.py и upgrade_code/*.py — другой механизм. Он переписывает исходники addons при переходе версий: tree→list view naming, SQL constraints, JSON-RPC routes, translation constructs, dynamic dates, deprecated properties и отдельные налоговые API. Он не изменяет живую DB schema и не заменяет migration scripts.
base называет себя kernel module, но архитектурно это DB-resident control plane поверх Python kernel. Его можно разделить на блоки:
| Блок | Модели/файлы | Роль в ядре |
|---|---|---|
| Метамодель | ir.model, ir.model.fields, ir.model.data, constraints/relations |
отражение Python models/fields и XML IDs в БД |
| Module state | ir.module.module, dependencies/categories |
install/upgrade/uninstall state machine |
| Security | users, groups, ir.model.access, ir.rule, API keys |
identity, ACL и row-level domains |
| HTTP control | ir.http |
DB-aware route matching, auth, dispatch/error hooks |
| Presentation metadata | views, menus, actions, QWeb, assets, reports | декларативное описание интерфейса и rendering contracts |
| Automation | ir.cron, autovacuum, sequences |
background jobs и housekeeping |
| Storage/config | attachments, binary, config parameters, defaults | filestore/blob access и системная конфигурация |
| Localization primitives | languages, countries, currencies | базовые shared entities, без прикладного поведения здесь |
| Tenancy/identity | companies, users, partners, devices | минимальные системные principals и company graph |
| Service support | mail server, logging, exports, filters, profiles | инфраструктурные сервисы базы |
res.partner здесь достаточно понимать как базовую shared identity/contact entity, на которую опираются пользователи и множество addons. Её поля, UI и бизнес-правила не нужны для понимания механики ядра. То же относится к bank/country/currency/company domain details.
Архитектурный вывод: часть важнейших policies (ir.rule, ir.http, ir.cron) расширяема обычным module inheritance и потому намеренно вынесена из hard-coded Python kernel в base.
tools — не один слой зависимости, а библиотека вертикалей.
translate.py: extraction Python/JS/XML, PO/CSV/tar readers/writers, import в БД, lazy gettext, language loading и translation upgrade queries;babel/*,i18n.py: extractors, locale parsing и locale-sensitive formatting;date_utils.py,float_utils.py,intervals.py: доменно-нейтральные операции дат, округления и множеств интервалов;arabic_reshaper/*: shaping арабского текста для renderers без native shaping.
xml_utils.py: parsers, cleanup, XSD validation/fetching;template_inheritance.py: поиск targets и применение XML inheritance specs;view_validation.py: Relax NG и semantic validation expressions/domains;rendering_tools.py: inline template syntax и QWeb conversion;js_transpiler.py,sourcemap_generator.py: преобразование JS modules в Odoo module format и source maps.
image.py: decode/resize/crop/quality/orientation и color profile;mimetypes.py: content sniffing, OOXML/OLE/SVG/WebP checks, extension correction и MIME neutering;pdf/*: facade над несколькими pypdf/PyPDF2 APIs и digital signature support;barcode.py: barcode rendering/check digits/font;mail.py: address parsing, header/message helpers и email normalization;rendering_tools.py,which.py: поиск внешних render executables и подготовка данных.
safe_eval.py компилирует выражение, проверяет bytecode whitelist, рекурсивно проверяет вложенные code objects, запрещает imports, опасные stores, dunder/frames/code attributes и даёт ограниченный globals set.
Это механизм снижения доступных возможностей для server actions/domains, но не процессная/контейнерная изоляция. Любой новый разрешённый object в globals расширяет attack surface.
lru.py,cache.py: LRU и registry method caches;func.py: lazy/class properties, synchronization, descriptor helpers;facade.py: proxy objects/functions для compatibility exports;set_expression.py: ленивые union/intersection expressions над ID sets;misc.py: широкая compatibility/utilities surface — filesystem, formatting, batching, locale, subprocess, collections;osutil.py,appdirs.py,urls.py,json.py,pycompat.py: OS paths/archive, app directories, URL joining, script-safe JSON, Python compatibility;query.py,sql.py: query/persistence primitives, рассмотренные выше;populate.py: массовое синтетическое заполнение моделей с variation factors;cloc.py: подсчёт строк addons и классификация файлов.
zeep/*: локальные wrappers/fixes SOAP client, WS-Addressing, WS-Security и WSDL helpers;_vendor/send_file.py,sessions.py,useragents.py: закреплённые implementations web utilities, чтобы поведение не дрейфовало вместе с dependency versions.
tools/profiler.py поддерживает collectors:
- SQL queries с full query, timing и Python stack;
- asynchronous periodic stack sampling;
- synchronous call/return tracing;
- QWeb directive tracing;
- memory/tracemalloc-oriented collection.
Collectors прикрепляются к thread hooks/cursor hooks, ExecutionContext добавляет semantic labels, а результат экспортируется в Speedscope-compatible data. Отдельные helpers отслеживают GC pauses и cache statistics.
tests/common.py строит собственный DB-aware test runtime:
TransactionCase: одна outer transaction на class, setup data один раз; каждый test method внутри savepoint, который откатывается; commit/rollback/close test cursor запрещены;SingleTransactionCase: все методы разделяют одну transaction до конца class;HttpCase: transaction semantics плюс HTTP server/browser coordination;ChromeBrowser: запускает headless Chrome, управляет DevTools Protocol/WebSocket, console/errors/screenshots/tours;Form: Python-эмуляция form client, defaults/onchange и x2many proxies.
Tests патчат Registry так, чтобы новые cursors могли оборачивать test cursor, отключают реальное signaling, очищают field/registry caches и вручную garbage-collect attachment filestore.
loader.py импортирует tests/test_*.py addons и upgrade tests, собирает только локально объявленные unittest classes/methods. TagsSelector поддерживает include/exclude по module/class/method/file/parameters. at_install suites запускаются в module load, post_install — после preload/finalization. suite.py — vendored упрощённый unittest suite с Odoo class lifecycle; result.py собирает stats, failures и reports.
odoo-bin / python -m odoo
→ import odoo
→ version check, UTC, GC, monkeypatch import hook
→ CLI command discovery
→ config CLI/env/file/default merge
→ logging and server-wide module import
→ server topology selection
→ optional Registry.new() for preload DBs
→ listen HTTP / start workers / start cron
select database
→ Registry(db) miss
→ lock Registry.new
→ module graph from DB state + manifests
→ import definition classes
→ build dynamic per-DB model classes
→ setup fields/dependencies/schema/hooks
→ publish ready registry
→ open cursor + Environment
→ match/auth/dispatch request
route match/auth
→ model ACL + record rules + field ACL
→ values in field cache, dirty marks, relational operations
→ modified() builds recompute work
→ flush fixed point
→ constraints/company checks
→ precommit hooks
→ PostgreSQL COMMIT
→ postcommit callbacks
→ publish cache/registry signaling
→ session/response finalization
serialization/deadlock/lock/ConcurrencyError
→ rollback DB
→ reset Transaction and Environments
→ reset request session/file streams
→ exponential jitter
→ rerun complete endpoint (до 5 попыток)
mark to install/upgrade/remove
→ Registry.new(update_module=True)
→ graph + migrations + Python import
→ rebuild model classes/fields
→ schema/data/translation/tests
→ per-module commit
→ registry sequence increment
→ другие процессы замечают signaling
→ rebuild their per-process registry
- Registry принадлежит базе и процессу, не всему cluster.
- Imported model class не равен runtime model class; последний собирается для registry.
- Record — singleton recordset, а не отдельный entity object.
- Environment неизменяем по identity/context, но разделяет mutable Transaction на cursor.
- Flush не commit. Raw SQL после ORM write обязан учитывать pending dirty fields.
- Field cache и
ormcache— разные кэши с разными сроками жизни и invalidation. sudoиwith_user— не одно и то же; company context — третья ось.- ACL, record rules, field groups и company checks независимы. Успех одной проверки не подразумевает другие.
- Compute должен объявить dependencies, включая context, иначе cache/recompute не узнают о причине изменения.
- Relation dependency требует before/after state, поэтому
modified()вызывается с разными фазами. - Readonly route может быть полностью повторён на primary, если обнаружилась запись.
- Request может быть повторён после rollback, поэтому внешний side effect до commit опасен.
- Install нескольких модулей не является одной глобальной транзакцией: loader коммитит завершённые modules.
base— часть минимальной рабочей системы, хотя технически расположен как addon.- Controller inheritance, как model inheritance, зависит от установленных addons конкретной БД.
safe_eval— ограниченный evaluator, не security sandbox уровня процесса.- Multi-worker cache coherence асинхронна на request boundaries и опирается на PostgreSQL signaling.
- Prefetch — свойство recordset lineage; случайное дробление recordsets меняет performance profile.
Нормальные extension points:
- addon manifest и dependency graph;
- model
_inherit/_inherits, fields и decorators; @routecontroller inheritance;ir.http,ir.rule,ir.cronи другие control-plane models;- manifest data/demo/assets и XML inheritance;
- init/pre/post/uninstall hooks;
- versioned migration scripts;
- named method caches и postcommit callbacks;
- CLI
Commandиз addon CLI package.
Опасные/низкоуровневые extension points:
- monkeypatches и process-wide
post_load; - прямое изменение registry classes/MRO после setup;
- raw SQL без
to_flush/flush/invalidation; - ручные commits внутри request/model methods;
- side effects до успешного commit;
- расширение globals/opcodes
safe_eval; - полагание на in-process cache в multi-worker topology.
| Вопрос | Начать с |
|---|---|
| Почему сервер стартует в таком режиме? | cli/server.py → tools/config.py → service/server.py |
| Почему addon не загружается? | modules/module.py → module_graph.py → loading.py |
| Откуда взялось/исчезло поле? | MetaModel → model_classes.py → field setup → ir.model.fields |
| Почему compute не пересчитался? | field depends → Registry TriggerTree → modified() → flush_all() |
| Почему запрос дал не те ids? | Domain.optimize_full() → record rules → Query → generated SQL |
| Почему значение старое? | transaction field cache vs registry ormcache vs interprocess signaling |
| Почему write виден в ORM, но не в SQL? | dirty cache и необходимость flush |
| Почему endpoint вызвался дважды? | readonly fallback или service.model.retrying() |
| Почему доступ запрещён? | auth → ACL → record rule → field group → company check |
| Почему другой worker не видит изменение структуры? | registry signaling sequence/check boundary |
| Почему установка частично сохранилась? | per-module commits в load_module_graph() |
| Почему тест не может commit? | TransactionCase cursor patches/savepoint cleanup |
Ниже — полный inventory вне odoo/addons/**. Описания намеренно кратки: детали уже сгруппированы по слоям выше.
__main__.py— entry pointpython -m odoo.init.py— ранняя инициализация пакета, runtime checks, monkeypatches, публичные constants.release.py— series/version/protocol и поддерживаемые Python/PostgreSQL versions.exceptions.py— общая иерархия пользовательских, access, missing, lock и concurrency ошибок.http.py— WSGI application, controllers/routes, requests, sessions, dispatchers, CSRF/RPC.sql_db.py— PostgreSQL connections/pools/cursors/transactions/savepoints/callbacks.netsvc.py— logging bootstrap, DB/performance log handlers и formatters.loglevels.py— дополнительные уровни logging.import_xml.rng— Relax NG grammar declarative module XML.
_monkeypatches/__init__.py— import hook и orchestration всех patches._monkeypatches/site.py— UTC/codecs и gevent/psycopg2 evented bootstrap._monkeypatches/ast.py— совместимость/ограничения Python AST._monkeypatches/bs4.py— нормализация BeautifulSoup._monkeypatches/csv.py— CSV dialect/field-size compatibility._monkeypatches/docutils.py— безопасное/стабильное поведение docutils._monkeypatches/email.py— исправления Python email parsing/serialization._monkeypatches/locale.py— предсказуемая locale handling._monkeypatches/lxml.py— XML/HTML parser behavior и compatibility._monkeypatches/mimetypes.py— детерминированная MIME database._monkeypatches/num2words.py— исправления локализованного преобразования чисел._monkeypatches/pytz.py— timezone compatibility._monkeypatches/re.py— regular-expression runtime compatibility._monkeypatches/stdnum.py— исправления python-stdnum._monkeypatches/urllib3.py— HTTP connection compatibility._monkeypatches/werkzeug.py— WSGI/request/response/session compatibility._monkeypatches/xlrd.py— чтение legacy Excel compatibility._monkeypatches/xlsxwriter.py— генерация XLSX compatibility._monkeypatches/xlwt.py— генерация XLS compatibility._monkeypatches/zeep.py— SOAP client compatibility/security fixes.
api/__init__.py— re-export Environment, decorators и API helpers.fields/__init__.py— public field classes иCommand.models/__init__.py— publicModel,AbstractModel,TransientModelи helpers.orm/__init__.py— упорядоченная сборка внутреннего ORM namespace.
cli/__init__.py— public CLI entry.cli/command.py— command registry, discovery, parsing и dispatcher.cli/server.py— production/default server command.cli/start.py— быстрый project-aware local start.cli/shell.py— interactive shell с DB environment.cli/help.py— command help.cli/db.py— database administration CLI.cli/module.py— module install/upgrade/list operations.cli/i18n.py— translation export/import/load operations.cli/neutralize.py— запуск neutralization SQL.cli/populate.py— synthetic data population.cli/cloc.py— addon source line counting.cli/scaffold.py— template renderer для нового addon.cli/deploy.py— zip и upload addon на server endpoint.cli/obfuscate.py— obfuscation/anonymization DB copy.cli/upgrade_code.py— sequential source rewrite driver.cli/templates/default/__init__.py.template— package init default addon.cli/templates/default/__manifest__.py.template— default addon manifest.cli/templates/default/controllers/__init__.py.template— controller package init.cli/templates/default/controllers/controllers.py.template— пример HTTP controller.cli/templates/default/demo/demo.xml.template— пример demo data.cli/templates/default/models/__init__.py.template— model package init.cli/templates/default/models/models.py.template— пример model definition.cli/templates/default/security/ir.model.access.csv.template— ACL skeleton.cli/templates/default/views/templates.xml.template— QWeb template skeleton.cli/templates/default/views/views.xml.template— view/action/menu skeleton.cli/templates/theme/__init__.py.template— theme package init.cli/templates/theme/__manifest__.py.template— theme manifest.cli/templates/theme/demo/pages.xml.template— demo pages.cli/templates/theme/static/src/scss/custom.scss.template— custom theme SCSS.cli/templates/theme/views/options.xml.template— theme options.cli/templates/theme/views/snippets.xml.template— theme snippets.cli/templates/l10n_payroll/__init__.py.template— payroll localization package init.cli/templates/l10n_payroll/__manifest__.py.template— payroll localization manifest.cli/templates/l10n_payroll/data/hr_payroll_structure_data.xml.template— structures skeleton.cli/templates/l10n_payroll/data/hr_payroll_structure_type_data.xml.template— structure types skeleton.cli/templates/l10n_payroll/data/hr_rule_parameters_data.xml.template— rule parameters skeleton.cli/templates/l10n_payroll/data/hr_salary_rule_category_data.xml.template— salary categories skeleton.cli/templates/l10n_payroll/data/hr_salary_rule_data.xml.template— salary rules skeleton.cli/templates/l10n_payroll/data/l10n_{{code}}_hr_payroll_demo.xml.template— localized demo skeleton.cli/templates/l10n_payroll/models/__init__.py.template— payroll models package init.cli/templates/l10n_payroll/models/hr_payslip.py.template— payslip extension skeleton.cli/templates/l10n_payroll/models/hr_payslip_worked_days.py.template— worked-days extension skeleton.cli/templates/l10n_payroll/models/hr_version.py.template— HR version extension skeleton.cli/templates/l10n_payroll/views/hr_payroll_report.xml.template— payroll report view skeleton.cli/templates/l10n_payroll/views/report_payslip_templates.xml.template— payslip QWeb skeleton.
modules/__init__.py— module subsystem exports/initialization.modules/module.py— addon namespace, Manifest, paths, dependency checks и Python import.modules/module_graph.py— dependency DAG и phase/order filtering.modules/loading.py— install/load/update/uninstall orchestration.modules/db.py— new DB SQL bootstrap и module metadata initialization.modules/migration.py— discovery/execution versioned migration scripts.modules/neutralize.py— aggregation/executionneutralize.sql.modules/registry/__init__.py— compatibility exports нового ORM Registry.
orm/models.py—MetaModel,BaseModel, recordsets, CRUD/search/schema и core model API.orm/model_classes.py— dynamic per-registry class construction и setup phases.orm/models_transient.py—AbstractModel,Model,TransientModelspecializations и vacuum.orm/registry.py— per-DB registry, setup, caches, signaling и schema coordination.orm/environments.py— Environment, Transaction, field cache, dirty/recompute state.orm/fields.py— base Field descriptor, setup, conversion, compute/inverse/related logic.orm/fields_misc.py— Boolean/Json/Id-like fields.orm/fields_numeric.py— Integer/Float/Monetary.orm/fields_textual.py— Char/Text/Html и translated JSONB values.orm/fields_temporal.py— Date/Datetime.orm/fields_binary.py— Binary/Image и attachment/image interaction.orm/fields_selection.py— Selection.orm/fields_reference.py— Reference и Many2oneReference.orm/fields_relational.py— Many2one/One2many/Many2many.orm/fields_properties.py— dynamic JSONB Properties/definitions.orm/domains.py— Domain AST, optimization и SQL conversion.orm/commands.py— typed x2many command protocol.orm/decorators.py— API/model metadata decorators.orm/table_objects.py— Constraint/Index/UniqueIndex descriptors.orm/identifiers.py—NewIdи ID typing.orm/types.py— shared ORM type aliases.orm/utils.py— name validation, field expression и origin/id helpers.
osv/__init__.py— legacy namespace exports.osv/expression.py— compatibility API поверх domain/expression machinery.
service/__init__.py— service package marker/exports.service/server.py— threaded, gevent, prefork servers, workers, cron и lifecycle.service/model.py— model RPC dispatch, public method checks и transaction retrying.service/common.py— version/auth legacy common service.service/db.py— database/filestore administration service.service/security.py— session token и device/audit helpers.
tests/__init__.py— test API exports и tagging helpers.tests/case.py— vendored/custom unittest case primitives.tests/common.py— DB/HTTP/Chrome test cases и общие assertions/helpers.tests/form.py— Form/onchange client emulator и x2many proxies.tests/loader.py— addon/upgrade test discovery и suite construction.tests/suite.py— Odoo-aware vendored TestSuite lifecycle.tests/tag_selector.py— include/exclude tag parser/matcher.tests/result.py— test result, stats, logging и report aggregation.tests/shell.py— запуск install-position suites из shell.tests/test_cursor.py— cursor wrapper для test transaction reuse.tests/test_module_operations.py— kernel tests module install/upgrade/uninstall semantics.tests/dummy.js— fixture определения JS file/module.tests/dummy.xml— fixture XML data/template parsing.
tools/__init__.py— curated public utility facade.tools/_vendor/__init__.py— vendored namespace.tools/_vendor/send_file.py— pinned HTTP file response implementation.tools/_vendor/sessions.py— pinned filesystem session store primitives.tools/_vendor/useragents.py— user-agent parsing.tools/appdirs.py— cross-platform data/config/cache/log directories.tools/constants.py— shared constants, regexes и sentinels.tools/urls.py— safe normalized URL joining/dot-segment handling.tools/json.py— Odoo JSON codec/defaults и script-safe output.tools/pycompat.py— small Python/CSV/text compatibility layer.tools/osutil.py— filename sanitization и deterministic ZIP creation.tools/which.py— external executable discovery.tools/facade.py— lazy proxy attributes/functions для compatibility APIs.tools/func.py— descriptors, lazy values, locks, cached-property reset.tools/gc.py— GC timing/info и temporary disabling context.tools/lru.py— LRU mapping implementation.tools/cache.py—ormcachedecorators, counters и diagnostics.tools/set_expression.py— lazy unions/intersections/leaves ID sets.tools/intervals.py— interval algebra.tools/parse_version.py— semantic-ish Odoo version parsing/comparison.tools/date_utils.py— date range, period, arithmetic и formatting helpers.tools/float_utils.py— precision-aware float compare/round/split.tools/misc.py— broad shared filesystem, formatting, batching, locale и collection helpers.
tools/sql.py— composable SQL и schema helpers.tools/query.py— lazy SELECT/join/where/order query representation.tools/convert.py— XML/CSV/SQL module data import.tools/cloc.py— addon code line analysis.tools/populate.py— high-volume synthetic model population.tools/safe_eval.py— restricted Python expression evaluator.tools/js_transpiler.py— JS ES-module to Odoo-module transformation.tools/sourcemap_generator.py— source-map encoding/generation.
tools/i18n.py— locale formatting и Python↔JS locale conversion.tools/translate.py— extraction, PO/CSV I/O, DB translations, gettext/lazy translate.tools/babel/__init__.py— Babel integration namespace.tools/babel/javascript_extractor.py— translatable terms из JavaScript.tools/babel/python_extractor.py— translatable terms из Python.tools/arabic_reshaper/__init__.py— public Arabic shaping API.tools/arabic_reshaper/letters.py— Arabic forms/ligature data.tools/xml_utils.py— XML parsing, cleanup и schema validation.tools/template_inheritance.py— XML inheritance spec application.tools/view_validation.py— view schemas и semantic expression validation.tools/rendering_tools.py— inline templates и QWeb-compatible rendering helpers.
tools/mail.py— email addresses, headers, bodies и message utilities.tools/mimetypes.py— content-based MIME detection и filename safety.tools/image.py— image transforms, validation и color management.tools/barcode.py— barcode rendering/checking.tools/pdf/__init__.py— version-independent PDF facade.tools/pdf/_pypdf.py— adapter современной pypdf.tools/pdf/_pypdf2_1.py— adapter старого PyPDF2 API line.tools/pdf/_pypdf2_2.py— adapter нового PyPDF2 API line.tools/pdf/signature.py— PDF digital signing.tools/rendering_tools.py— также inline report rendering helpers.tools/test_reports.py— test helpers report actions/output.tools/data/files/sRGB2014.icc— ICC color profile для image/PDF processing.tools/data/files/sRGB2014.icc.LICENSE— лицензия встроенного ICC profile.
tools/profiler.py— SQL/periodic/sync/QWeb/memory collectors и profiler orchestration.tools/speedscope.py— Speedscope profile representation/export.tools/zeep/__init__.py— SOAP wrapper exports.tools/zeep/client.py— patched Zeep client behavior.tools/zeep/exceptions.py— SOAP wrapper exceptions.tools/zeep/helpers.py— SOAP value serialization helpers.tools/zeep/ns.py— namespaces/constants.tools/zeep/wsa.py— WS-Addressing helpers.tools/zeep/wsdl/__init__.py— WSDL wrapper namespace.tools/zeep/wsdl/utils.py— WSDL parsing utilities.tools/zeep/wsse/__init__.py— WS-Security namespace.tools/zeep/wsse/username/__init__.py— UsernameToken integration.
upgrade/.gitkeep— резервирует namespaceodoo.upgradeдля внешних upgrade packages.upgrade_code/17.5-00-example.py— example source-upgrade script/framework contract.upgrade_code/17.5-01-tree-to-list.py— migration старогоtreeview terminology кlist.upgrade_code/18.1-00-sql-constraint.py— преобразование SQL constraint declarations.upgrade_code/18.1-02-route-jsonrpc.py— обновление JSON-RPC route declarations.upgrade_code/18.2-00-l10n-translate.py— обновление localization translation patterns.upgrade_code/18.3-00-l10n-fiscal-position-taxes.py— обновление fiscal-position tax code patterns.upgrade_code/18.5-00-deprecated-properties.py— замена deprecated model/field properties.upgrade_code/18.5-00-domain-dynamic-dates.py— перевод dynamic-date domain syntax.upgrade_code/18.5-00-no-tax-tag-invert.py— удаление устаревшего tax-tag inversion pattern.
Минимальное точное описание Odoo звучит так:
Odoo — PostgreSQL-центричный, модульно компилируемый application runtime. Manifests и Python definition classes превращаются в отдельный Registry динамических моделей для каждой базы; recordsets исполняются внутри Environment/Transaction с descriptor fields, prefetch, cache и dependency recompute; HTTP/RPC/cron процессы оборачивают этот runtime аутентификацией, policy из
base, retry/commit protocol и межпроцессным signaling.
Если держать в голове четыре объекта — Registry, Model class, Environment/Transaction и Request — большая часть остальных файлов раскладывается вокруг них: module loader строит Registry; ORM наполняет поведение Model class; cursor/cache обеспечивают Transaction; HTTP/server создают и завершают Request.
Подробный документ: /private/tmp/odoo-19-web-core-architecture-ru.md.
Фраза выше «web-клиент исключён» описывала исходный scope, но не архитектурную классификацию. /Users/max/Dev/odoo_19/addons/web действительно находится в соседнем addon namespace, однако функционально является третьей опорной частью ядра:
техническое ядро Odoo
= Python runtime (`odoo/`)
+ системная метамодель (`odoo/addons/base`)
+ клиентский/представительный runtime (`addons/web`)
web зависит только от base, устанавливается автоматически и поставляет общий Web Client. Это инфраструктурный addon, а не бизнес-модуль.
Аудит 2 319 файлов web добавил следующие логические уровни:
- asset graph и собственный JavaScript module loader;
- HTML/session bootstrap и запуск Owl environment;
- generic JSON-RPC/ORM transport;
- серверный Web ORM protocol:
web_read,web_search_read,web_save,web_read_group, search-panel и genericonchange; - browser registries, dependency-started services,
patch()и template inheritance; - action stack, menus, router, breadcrumbs и deep links;
- загрузка/компиляция XML views;
- client relational draft model с x2many commands;
- SearchModel, filters, group-by, favorites и search panels;
- form/list/kanban/calendar/graph/pivot и field/widget registry;
- binary/export/report protocols;
- public interaction и PWA shell;
- design system, localization, responsive/RTL/dark/print;
- Hoot/QUnit/tours/mock-server test runtime.
В полной Odoo работают две динамические композиционные машины. Серверный Registry собирает итоговые Python-модели из вкладов addons. Web runtime собирает итоговое клиентское приложение из asset contributions, JS registries, services, Owl templates, patches и разрешённых сервером XML views.
UI-семантика поэтому распределена между сервером и браузером. XML view сам по себе недостаточен: его исполняют Web ORM projection/onchange protocol, action/view/search engines, field registry и client Record/StaticList. Это принципиально для любого анализа совместимости или попытки переписать Odoo.
Исходные 207 файлов и 63 542 строки относятся только к Python core вне addons. web добавляет 2 319 tracked-файлов, но в них находятся крупные vendored libraries, fonts, browser maps, переводы, данные emoji и около 225 тыс. строк frontend tests. Содержательное production ядро web — примерно 6,5 тыс. строк Python backend плюс около 117 тыс. строк JS/TS, 10,6 тыс. строк XML templates и 16,8 тыс. строк SCSS/CSS. Эти величины нужны для масштаба, а не для оценки качества или прямого суммирования.
Полное ядро Odoo — это не только компилятор моделей и транзакционный сервер. Это также универсальная виртуальная машина интерфейсов: сервер публикует metadata-oriented protocol, а браузер строит из него action-driven приложение и поддерживает до сохранения виртуальный граф записей. Бизнес-addons расширяют обе стороны одновременно.