Skip to content

Commit ac5bde3

Browse files
author
Callum Dickinson
committed
Replace supplementary record/manager classes with mixins
Records in Odoo can have some additional fields that are shared across many different record models using model inheritance. To support adding these kinds of shared fields (and related methods) to record/manager classes in the OpenStack Odoo Client library in a more modular way, add support for the use of **mixins** to take advantage of Python's multiple inheritance to add such fields and methods to custom record/manager classes. To make type-hinted record manager mixins possible, a **protocol** class called `RecordManagerProtocol` had to be created documenting the attributes and methods on the `RecordManagerBase` class intended for use by implementing classes. This protocol is used to set the type of the `self` parameter on mixin methods. `RecordManagerBase` now subclasses this protocol. The `NamedRecordManagerBase` base class has been reimplemented using mixins to not only utilise this paradigm inside the library itself, but also demonstrate its usage in a simple and practical way for anyone looking to write their own mixins. The `get_by_unique_field` method provided by `RecordManagerWithUniqueFieldBase` has been moved into `RecordManagerBase` to make it available for use in any custom manager class. `CodedRecordManagerBase` has been removed and its implemented moved directly into `ReferralCodeManager`, as it is not used by anything else.
1 parent 33e5d51 commit ac5bde3

28 files changed

+1174
-832
lines changed

openstack_odooclient/__init__.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@
1818
from .base.client import ClientBase
1919
from .base.record import FieldAlias, ModelRef, RecordBase
2020
from .base.record_manager import RecordManagerBase
21-
from .base.record_manager_coded import CodedRecordManagerBase
22-
from .base.record_manager_named import NamedRecordManagerBase
23-
from .base.record_manager_with_unique_field import (
24-
RecordManagerWithUniqueFieldBase,
25-
)
2621
from .client import Client
2722
from .exceptions import (
2823
ClientError,
@@ -77,6 +72,7 @@
7772
VolumeDiscountRangeManager,
7873
)
7974
from .managers.voucher_code import VoucherCode, VoucherCodeManager
75+
from .mixins.named_record import NamedRecordManagerMixin, NamedRecordMixin
8076

8177
__all__ = [
8278
"AccountMove",
@@ -86,7 +82,6 @@
8682
"Client",
8783
"ClientBase",
8884
"ClientError",
89-
"CodedRecordManagerBase",
9085
"Company",
9186
"CompanyManager",
9287
"Credit",
@@ -106,7 +101,8 @@
106101
"GrantTypeManager",
107102
"ModelRef",
108103
"MultipleRecordsFoundError",
109-
"NamedRecordManagerBase",
104+
"NamedRecordManagerMixin",
105+
"NamedRecordMixin",
110106
"Partner",
111107
"PartnerCategory",
112108
"PartnerCategoryManager",
@@ -123,7 +119,6 @@
123119
"ProjectManager",
124120
"RecordBase",
125121
"RecordManagerBase",
126-
"RecordManagerWithUniqueFieldBase",
127122
"RecordNotFoundError",
128123
"ReferralCode",
129124
"ReferralCodeManager",

openstack_odooclient/base/record.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ def _decode_value(cls, type_hint: Any, value: Any) -> Any:
465465
# Not suitable for handling complicated union structures.
466466
# TODO(callumdickinson): Find a way to handle complicated
467467
# union structures more smartly.
468-
if value_type is Union:
468+
if value_type is Union or value_type is UnionType:
469469
attr_union_types = get_type_args(type_hint)
470470
if len(attr_union_types) == 2: # noqa: PLR2004
471471
# T | None
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Copyright (C) 2025 Catalyst Cloud Limited
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12+
# implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
from __future__ import annotations
17+
18+
from .base import RecordManagerBase
19+
from .protocol import RecordManagerProtocol
20+
from .types import FilterCriterion, Record
21+
22+
__all__ = [
23+
"FilterCriterion",
24+
"Record",
25+
"RecordManagerBase",
26+
"RecordManagerProtocol",
27+
]

0 commit comments

Comments
 (0)