-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
[ADD] orm_forward_compatibility: Odoo 19+ ORM helpers backported to 18.0 #3701
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 18.0
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| ========================= | ||
| ORM Forward Compatibility | ||
| ========================= | ||
|
|
||
| .. | ||
| !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| !! This file is generated by oca-gen-addon-readme !! | ||
| !! changes will be overwritten. !! | ||
| !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| !! source digest: sha256:69068e4f7be049656b46a19592cfb15d2468bddb3e38f8522adbb63adf8ba386 | ||
| !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
|
|
||
| .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png | ||
| :target: https://odoo-community.org/page/development-status | ||
| :alt: Beta | ||
| .. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png | ||
| :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html | ||
| :alt: License: LGPL-3 | ||
| .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github | ||
| :target: https://github.com/OCA/server-tools/tree/18.0/orm_forward_compatibility | ||
| :alt: OCA/server-tools | ||
| .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png | ||
| :target: https://translation.odoo-community.org/projects/server-tools-18-0/server-tools-18-0-orm_forward_compatibility | ||
| :alt: Translate me on Weblate | ||
| .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png | ||
| :target: https://runboat.odoo-community.org/builds?repo=OCA/server-tools&target_branch=18.0 | ||
| :alt: Try me on Runboat | ||
|
|
||
| |badge1| |badge2| |badge3| |badge4| |badge5| | ||
|
|
||
| This module backports a subset of the Odoo 19+ ORM API onto Odoo 18.0. | ||
| Backporting a module to 18.0 then requires fewer adaptations. | ||
|
|
||
| Backported so far: | ||
|
|
||
| - ``Domain``, the domain object introduced in Odoo 19. | ||
| - The typed ``ir.config_parameter`` getters ``get_str``, ``get_int``, | ||
| ``get_float`` and ``get_bool``, which return a typed value or a | ||
| default instead of ``False``. | ||
|
|
||
| **Table of contents** | ||
|
|
||
| .. contents:: | ||
| :local: | ||
|
|
||
| Usage | ||
| ===== | ||
|
|
||
| **Domain** | ||
|
|
||
| Replace the Odoo 19 import ``from odoo.fields import Domain`` and leave | ||
| the rest untouched: | ||
|
|
||
| .. code:: python | ||
|
|
||
| from odoo.addons.orm_forward_compatibility import Domain | ||
|
|
||
| domain = Domain("partner_id", "=", partner.id) & Domain([("state", "=", "done")]) | ||
|
|
||
| Not supported yet: | ||
|
|
||
| - Relative-date literals | ||
| - custom SQL domains | ||
| - ``any!`` / ``not any!`` operators | ||
|
|
||
| **Typed ir.config_parameter getters** | ||
|
|
||
| .. code:: python | ||
|
|
||
| limit = self.env["ir.config_parameter"].sudo().get_int("my_module.limit", 20) | ||
|
|
||
| Bug Tracker | ||
| =========== | ||
|
|
||
| Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/issues>`_. | ||
| In case of trouble, please check there if your issue has already been reported. | ||
| If you spotted it first, help us to smash it by providing a detailed and welcomed | ||
| `feedback <https://github.com/OCA/server-tools/issues/new?body=module:%20orm_forward_compatibility%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. | ||
|
|
||
| Do not contact contributors directly about support or help with technical issues. | ||
|
|
||
| Credits | ||
| ======= | ||
|
|
||
| Authors | ||
| ------- | ||
|
|
||
| * Camptocamp | ||
|
|
||
| Contributors | ||
| ------------ | ||
|
|
||
| - `Camptocamp <https://www.camptocamp.com>`__: | ||
|
|
||
| - David Gallay <david.gallay@camptocamp.com> | ||
|
|
||
| Maintainers | ||
| ----------- | ||
|
|
||
| This module is maintained by the OCA. | ||
|
|
||
| .. image:: https://odoo-community.org/logo.png | ||
| :alt: Odoo Community Association | ||
| :target: https://odoo-community.org | ||
|
|
||
| OCA, or the Odoo Community Association, is a nonprofit organization whose | ||
| mission is to support the collaborative development of Odoo features and | ||
| promote its widespread use. | ||
|
|
||
| This module is part of the `OCA/server-tools <https://github.com/OCA/server-tools/tree/18.0/orm_forward_compatibility>`_ project on GitHub. | ||
|
|
||
| You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Copyright 2026 Camptocamp SA | ||
| # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html). | ||
| from . import models | ||
| from .domain import Domain |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| # Copyright 2026 Camptocamp SA | ||
| # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html). | ||
| { | ||
| "name": "ORM Forward Compatibility", | ||
| "version": "18.0.1.0.0", | ||
| "development_status": "Beta", | ||
| "summary": "Backport newer-version ORM helpers (Domain) onto Odoo 18.0", | ||
| "author": "Camptocamp, Odoo Community Association (OCA)", | ||
| "website": "https://github.com/OCA/server-tools", | ||
| "license": "LGPL-3", | ||
| "category": "Hidden/Dependency", | ||
| "depends": ["base"], | ||
| "installable": True, | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| # Copyright 2026 Camptocamp SA | ||
| # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html). | ||
| """Minimal ``Domain`` shim backporting the Odoo 19+ ``odoo.fields.Domain`` API. | ||
|
|
||
| Odoo 19 replaced list-domains by a ``Domain`` AST object (``odoo/orm/domains.py``, | ||
| ~2000 lines). Backported modules written against 19/20 import it as | ||
| ``from odoo.fields import Domain``. Rather than porting the whole engine, this | ||
| shim reimplements *only* the surface those modules use, delegating to 18's | ||
| ``odoo.osv.expression``. | ||
|
|
||
| Because ``Domain`` subclasses ``list`` and normalises itself to a plain 18 | ||
| list-domain, a shim instance can be passed straight to ``search``/``_search``. | ||
|
|
||
| Supported surface (extend as new call sites appear): | ||
| - ``Domain([('a', '=', 1), ...])`` and ``Domain('a', '=', 1)`` constructors | ||
| - ``&`` / ``|`` / ``~`` operators | ||
| - ``Domain.AND(iterable)`` / ``Domain.OR(iterable)`` | ||
| - ``Domain.TRUE`` / ``Domain.FALSE`` | ||
| - ``.optimize_full(model)`` -> validates against the model, returns self | ||
|
|
||
| NOT supported (keep such call sites hand-adapted on 18): | ||
| - relative-date literals in leaves (e.g. ``('date', '<', '-1d')``) | ||
| - custom SQL domains, ``any!``/``not any!`` internal operators | ||
| """ | ||
|
|
||
| from odoo.osv import expression | ||
|
|
||
|
|
||
| class Domain(list): | ||
| def __init__(self, *args): | ||
| if len(args) == 3: | ||
| domain = [tuple(args)] | ||
| elif len(args) == 1: | ||
| arg = args[0] | ||
| if isinstance(arg, Domain): | ||
| domain = list(arg) | ||
| elif arg is True or arg == []: | ||
| domain = list(expression.TRUE_DOMAIN) | ||
| elif arg is False: | ||
| domain = list(expression.FALSE_DOMAIN) | ||
| elif isinstance(arg, list | tuple): | ||
| domain = expression.normalize_domain(list(arg)) | ||
| else: | ||
| raise TypeError(f"Domain() invalid argument type: {arg!r}") | ||
| else: | ||
| raise TypeError(f"Domain() invalid arguments: {args!r}") | ||
| super().__init__(domain) | ||
|
|
||
| def __and__(self, other): | ||
| return Domain(expression.AND([list(self), list(Domain(other))])) | ||
|
|
||
| __rand__ = __and__ | ||
|
|
||
| def __or__(self, other): | ||
| return Domain(expression.OR([list(self), list(Domain(other))])) | ||
|
|
||
| __ror__ = __or__ | ||
|
|
||
| def __invert__(self): | ||
| return Domain(["!"] + list(self)) | ||
|
|
||
| @staticmethod | ||
| def AND(items): | ||
| return Domain(expression.AND([list(Domain(item)) for item in items])) | ||
|
|
||
| @staticmethod | ||
| def OR(items): | ||
| return Domain(expression.OR([list(Domain(item)) for item in items])) | ||
|
|
||
| def optimize_full(self, model): | ||
| """Validate the domain against ``model`` (raises on unknown fields).""" | ||
| model._where_calc(list(self)) | ||
| return self | ||
|
|
||
|
|
||
| # v19/v20 call sites reference ``Domain.TRUE`` / ``Domain.FALSE`` as attributes. | ||
| Domain.TRUE = Domain(True) | ||
| Domain.FALSE = Domain(False) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| # Copyright 2026 Camptocamp SA | ||
| # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html). | ||
| from . import ir_config_parameter |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| # Copyright 2026 Camptocamp SA | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not copied from Odoo's code. The code here only fill the gap by mimicking V20 interface. |
||
| # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html). | ||
| from odoo import models | ||
| from odoo.tools import str2bool | ||
|
|
||
|
|
||
| class IrConfigParameter(models.Model): | ||
| _inherit = "ir.config_parameter" | ||
|
|
||
| def get_str(self, key, default=""): | ||
| value = self.get_param(key) | ||
| return default if value is False else str(value) | ||
|
|
||
| def get_int(self, key, default=0): | ||
| value = self.get_param(key) | ||
| if value is False: | ||
| return default | ||
| try: | ||
| return int(value) | ||
| except (TypeError, ValueError): | ||
| return default | ||
|
|
||
| def get_float(self, key, default=0.0): | ||
| value = self.get_param(key) | ||
| if value is False: | ||
| return default | ||
| try: | ||
| return float(value) | ||
| except (TypeError, ValueError): | ||
| return default | ||
|
|
||
| def get_bool(self, key, default=False): | ||
| value = self.get_param(key) | ||
| return default if value is False else str2bool(value, default) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| [build-system] | ||
| requires = ["whool"] | ||
| build-backend = "whool.buildapi" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| - [Camptocamp](https://www.camptocamp.com): | ||
| - David Gallay \<<david.gallay@camptocamp.com>\> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| This module backports a subset of the Odoo 19+ ORM API onto Odoo 18.0. | ||
| Backporting a module to 18.0 then requires fewer adaptations. | ||
|
|
||
| Backported so far: | ||
|
|
||
| - `Domain`, the domain object introduced in Odoo 19. | ||
| - The typed `ir.config_parameter` getters `get_str`, `get_int`, `get_float` and | ||
| `get_bool`, which return a typed value or a default instead of `False`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| **Domain** | ||
|
|
||
| Replace the Odoo 19 import `from odoo.fields import Domain` and leave the rest untouched: | ||
|
|
||
| ``` python | ||
| from odoo.addons.orm_forward_compatibility import Domain | ||
|
|
||
| domain = Domain("partner_id", "=", partner.id) & Domain([("state", "=", "done")]) | ||
| ``` | ||
|
|
||
| Not supported yet: | ||
| - Relative-date literals | ||
| - custom SQL domains | ||
| - `any!` / `not any!` operators | ||
|
|
||
| **Typed ir.config_parameter getters** | ||
|
|
||
| ``` python | ||
| limit = self.env["ir.config_parameter"].sudo().get_int("my_module.limit", 20) | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which kind of impact has such limitation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personnally didn't need them.
any!isn't popular yet, and-1dis usually done with date/datetime and timedelta.