@@ -562,59 +562,112 @@ from .parent import Parent # noqa: E402
562562
563563### Odoo Version Compatibility
564564
565- Major releases of Odoo may change the database models to introduce
566- new functionality.
565+ * Changed in version 0.3.0: Replaced ` _field_mapping ` with the
566+ ` VersionMapping ` annotation. *
567567
568- The way models usually change in a backwards-incompatible way is
569- that fields are renamed so that they are referenced using another name,
570- without providing an alias for the old one.
568+ Major releases of Odoo may change aspects of the API and database schema
569+ to introduce new functionality, fix bugs and so on. These changes can be
570+ difficult to manage when upgrading as generally the provisions for backwards
571+ compatibility are limited.
571572
572- In the OpenStack Odoo Client library, this is handled by defining
573- the ` _field_mapping ` attribute on the record class.
573+ The OpenStack Odoo Client library for Python provides features that allow
574+ record types and managers to be configured such that applications using the
575+ library do not need to be aware of differences between Odoo versions, which
576+ hopefully makes upgrading major Odoo versions easier.
574577
575- ``` python
576- _field_mapping: dict[str | None , dict[str , str ]]
577- ```
578+ #### Version Mappings
579+
580+ One way models can change in a backwards-incompatible way is that
581+ fields are renamed so that they are referenced using another name,
582+ without providing an alias for the old one.
578583
579- The ` _field_mapping ` attribute is a nested dictionary structure used
580- to define local-to-remote field name mappings.
584+ These differences can be handled using the ` VersionMapping ` annotation.
585+ This annotation allows you to configure Odoo version differences for specific
586+ fields on a model, as shown in the example below.
581587
582588``` python
583589from __future__ import annotations
584590
585- from openstack_odooclient import RecordBase
591+ from typing import Annotated
592+
593+ from openstack_odooclient import RecordBase, VersionMapping
586594
587595class CustomRecord (RecordBase[" CustomRecordManager" ]):
588- custom_field: str
596+ custom_field: Annotated[ str , VersionMapping( " <18.0 " , " old_custom_field " )]
589597 """ Description of the field."""
590598
591599 custom_field_2: int
592600 """ Description of the second field."""
593601
594602 custom_field_3: float
595603 """ Description of the third field."""
604+ ```
596605
597- _field_mapping = {
598- # The Odoo version for which to generate the mapping.
599- " 13.0" : {
600- # Key is local field name. Value is the field name in Odoo 13.
601- " custom_field" : " old_custom_field" ,
602- }
603- # Use None to provide a mapping to use for all Odoo versions.
604- None : {
605- " custom_field_2" : " old_custom_field_2" ,
606- },
607- # custom_field_3 is not defined here.
608- # The field name will be used as-is on all Odoo versions.
609- }
606+ By default ` custom_field ` will be used as the field name to query from Odoo,
607+ but if the connected Odoo server version satisfies the given version
608+ constraint (in this case it would match for Odoo 17 and earlier),
609+ ` old_custom_field ` is used instead.
610+
611+ Clients only need to reference the field name defined on the record object;
612+ the Python OpenStack Odoo Client library will transparently convert between
613+ the local and the correct remote field names, depending on the server's
614+ version.
615+
616+ When defining the annotation, set the first argument to the version specifier
617+ that defines the set of versions to match, and set the second argument to the
618+ name of the field to use.
619+
620+ ``` python
621+ from typing import Annotated
622+
623+ from openstack_odooclient import RecordBase, User, VersionMapping
624+
625+ class CustomRecord (RecordBase[" CustomRecordManager" ]):
626+ name: Annotated[str , VersionMapping(" <18.0" , " old_name" )]
610627```
611628
612- Mappings can be added for specific Odoo versions, or by using ` None ` ,
613- mappings that apply to all Odoo versions can be defined.
629+ For [ model refs] ( #model-refs ) , the version mapping applies to the model ref
630+ field specified in the ` ModelRef ` annotation. The version mapping only needs
631+ to be defined on ** one** of the defined model ref fields (the same version
632+ mapping will be used for all of them). It is recommended to add it to the
633+ field representing the record ID (or list of record IDs), as shown below.
614634
615- When the Odoo Client library interfaces with Odoo, it will automatically find
616- and use the correct field name to present based on the server version
617- and the record class's field mapping.
635+ ``` python
636+ from typing import Annotated
637+
638+ from openstack_odooclient import (
639+ ModelRef,
640+ RecordBase,
641+ User,
642+ VersionMapping,
643+ )
644+
645+ class CustomRecord (RecordBase[" CustomRecordManager" ]):
646+ user_id: Annotated[
647+ int ,
648+ ModelRef(" user_id" , User),
649+ VersionMapping(" <18.0" , " old_user_id" ),
650+ ]
651+ user_name: Annotated[str , ModelRef(" user_id" , User)]
652+ user: Annotated[User, ModelRef(" user_id" , User)]
653+ ```
654+
655+ Multiple version mappings can be defined for a single field.
656+ Version mappings are evaluated in order, and the first one that
657+ matches is used.
658+
659+ ``` python
660+ from typing import Annotated
661+
662+ from openstack_odooclient import RecordBase, User, VersionMapping
663+
664+ class CustomRecord (RecordBase[" CustomRecordManager" ]):
665+ name: Annotated[
666+ str ,
667+ VersionMapping(" <14.0" , " old_name1" ),
668+ VersionMapping(" >=14.0,<18.0" , " old_name2" ),
669+ ]
670+ ```
618671
619672### Record Methods
620673
@@ -1093,13 +1146,14 @@ class CustomClient(Client):
10931146 custom_users: CustomUserManager
10941147```
10951148
1096- Due to the Odoo Client library using type hints to determine what record classes to use,
1097- and the type hints being physically defined in code to allow type analysis tools such as Mypy
1098- and Pyright to properly evaluate the source, * existing* references on * existing* record classes
1099- cannot be automatically updated to use the custom versions.
1149+ Due to the Odoo Client library using type hints to determine what record
1150+ classes to use, and the type hints being physically defined in code to allow
1151+ static analysis tools such as Mypy to properly evaluate the source, * existing*
1152+ references on * existing* record classes cannot be automatically updated to use
1153+ the custom versions.
11001154
1101- However, it is possible to ** cast** a record object of the base type into the custom type
1102- using the record class's ` from_record_obj ` class method.
1155+ However, it is possible to ** cast** a record object of the base type into the
1156+ custom type using the record class's ` from_record_obj ` class method.
11031157
11041158``` python
11051159>> > odoo_client = CustomClient(... )
@@ -1113,5 +1167,5 @@ CustomUser(record={'id': 1234, 'custom_field': 'Hello, world!', ...}, fields=Non
11131167' Hello, world!'
11141168```
11151169
1116- This should cover the majority of use cases where custom add-ons add new functionality
1117- to existing models.
1170+ This should cover the majority of use cases where custom add-ons add new
1171+ functionality to existing models.
0 commit comments