Skip to content

Commit 3b3aaf7

Browse files
committed
[CHG] pydantic: drop support pydantic v1
1 parent d592160 commit 3b3aaf7

9 files changed

Lines changed: 86 additions & 313 deletions

File tree

.github/workflows/test.yml

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,9 @@ jobs:
3737
include:
3838
- container: ghcr.io/oca/oca-ci/py3.10-odoo17.0:latest
3939
name: test with Odoo
40-
pydantic2: "true"
4140
- container: ghcr.io/oca/oca-ci/py3.10-ocb17.0:latest
42-
name: test with OCB and pydantic > 2.0
41+
name: test with OCB
4342
makepot: "true"
44-
pydantic2: "true"
45-
- container: ghcr.io/oca/oca-ci/py3.10-ocb17.0:latest
46-
name: test with OCB and pydantic < 2.0
47-
exclude: "fastapi"
4843
services:
4944
postgres:
5045
image: postgres:12.0
@@ -54,8 +49,6 @@ jobs:
5449
POSTGRES_DB: odoo
5550
ports:
5651
- 5432:5432
57-
env:
58-
EXCLUDE: "${{ matrix.exclude }}"
5952
steps:
6053
- uses: actions/checkout@v4
6154
with:
@@ -68,12 +61,6 @@ jobs:
6861
run: manifestoo -d . check-dev-status --default-dev-status=Beta
6962
- name: Initialize test db
7063
run: oca_init_test_database
71-
- name: Ensure pydantic version < 2
72-
run: pip install "pydantic<2.0"
73-
if: ${{ matrix.pydantic2 != 'true' }}
74-
- name: Ensure pydantic version > 2
75-
run: pip install "pydantic>2.0"
76-
if: ${{ matrix.pydantic2 == 'true' }}
7764
- name: Run tests
7865
run: oca_run_tests
7966
- uses: codecov/codecov-action@v4

pydantic/README.rst

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Pydantic
2929
|badge1| |badge2| |badge3| |badge4| |badge5|
3030

3131
This addon provides a utility method that can be used to map odoo record
32-
to a `Pydantic model <https://docs.pydantic.dev/>`__.
32+
to a `Pydantic model (>= v2) <https://docs.pydantic.dev/>`__.
3333

3434
If you need to make your Pydantic models extendable at runtime, takes a
3535
look at the python package
@@ -49,10 +49,8 @@ To support pydantic models that map to Odoo models, Pydantic model
4949
instances can be created from arbitrary odoo model instances by mapping
5050
fields from odoo models to fields defined by the pydantic model.
5151

52-
To ease the mapping, the addon provide 2 utility classes:
53-
54-
- Using ``pydantic>2.0``,
55-
``odoo.addons.pydantic.utils.PydanticOdooBaseModel``:
52+
To ease the mapping, the addon provide an utility class (using
53+
``pydantic>2.0``) ``odoo.addons.pydantic.utils.PydanticOdooBaseModel``:
5654

5755
.. code:: python
5856
@@ -69,32 +67,6 @@ To ease the mapping, the addon provide 2 utility classes:
6967
user = self.env.user
7068
user_info = UserInfo.from_orm(user)
7169
72-
- Using ``pydantic<2.0``,
73-
``odoo.addons.pydantic.utils.GenericOdooGetter``:
74-
75-
.. code:: python
76-
77-
import pydantic
78-
from odoo.addons.pydantic import utils
79-
80-
class Group(pydantic.BaseModel):
81-
name: str
82-
83-
class Config:
84-
orm_mode = True
85-
getter_dict = utils.GenericOdooGetter
86-
87-
class UserInfo(pydantic.BaseModel):
88-
name: str
89-
groups: List[Group] = pydantic.Field(alias="groups_id")
90-
91-
class Config:
92-
orm_mode = True
93-
getter_dict = utils.GenericOdooGetter
94-
95-
user = self.env.user
96-
user_info = UserInfo.from_orm(user)
97-
9870
See the official `Pydantic documentation <https://docs.pydantic.dev/>`__
9971
to discover all the available functionalities.
10072

pydantic/__manifest__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"data": [],
1616
"demo": [],
1717
"external_dependencies": {
18-
"python": ["pydantic", "contextvars", "typing-extensions"]
18+
"python": ["pydantic>=2.0", "contextvars", "typing-extensions"]
1919
},
2020
"installable": True,
2121
}

pydantic/readme/DESCRIPTION.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
This addon provides a utility method that can be used to map odoo record
2-
to a [Pydantic model](https://docs.pydantic.dev/).
2+
to a [Pydantic model (>= v2)](https://docs.pydantic.dev/).
33

44
If you need to make your Pydantic models extendable at runtime, takes a
55
look at the python package

pydantic/readme/USAGE.md

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ instances can be created from arbitrary odoo model instances by mapping
33
fields from odoo models to fields defined by the pydantic model.
44

55

6-
To ease the mapping, the addon provide 2 utility classes:
7-
8-
* Using `pydantic>2.0`, `odoo.addons.pydantic.utils.PydanticOdooBaseModel`:
6+
To ease the mapping, the addon provide an utility class (using `pydantic>2.0`) `odoo.addons.pydantic.utils.PydanticOdooBaseModel`:
97

108
``` python
119
from odoo.addons.pydantic.utils import PydanticOdooBaseModel
@@ -22,31 +20,6 @@ user = self.env.user
2220
user_info = UserInfo.from_orm(user)
2321
```
2422

25-
* Using `pydantic<2.0`, `odoo.addons.pydantic.utils.GenericOdooGetter`:
26-
27-
``` python
28-
import pydantic
29-
from odoo.addons.pydantic import utils
30-
31-
class Group(pydantic.BaseModel):
32-
name: str
33-
34-
class Config:
35-
orm_mode = True
36-
getter_dict = utils.GenericOdooGetter
37-
38-
class UserInfo(pydantic.BaseModel):
39-
name: str
40-
groups: List[Group] = pydantic.Field(alias="groups_id")
41-
42-
class Config:
43-
orm_mode = True
44-
getter_dict = utils.GenericOdooGetter
45-
46-
user = self.env.user
47-
user_info = UserInfo.from_orm(user)
48-
```
49-
5023
See the official [Pydantic
5124
documentation](https://docs.pydantic.dev/) to discover all the
5225
available functionalities.

pydantic/static/description/index.html

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ <h1 class="title">Pydantic</h1>
371371
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
372372
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/rest-framework/tree/17.0/pydantic"><img alt="OCA/rest-framework" src="https://img.shields.io/badge/github-OCA%2Frest--framework-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/rest-framework-17-0/rest-framework-17-0-pydantic"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/rest-framework&amp;target_branch=17.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
373373
<p>This addon provides a utility method that can be used to map odoo record
374-
to a <a class="reference external" href="https://docs.pydantic.dev/">Pydantic model</a>.</p>
374+
to a <a class="reference external" href="https://docs.pydantic.dev/">Pydantic model (&gt;= v2)</a>.</p>
375375
<p>If you need to make your Pydantic models extendable at runtime, takes a
376376
look at the python package
377377
<a class="reference external" href="https://pypi.org/project/extendable_pydantic/">extendable-pydantic</a>
@@ -396,11 +396,8 @@ <h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
396396
<p>To support pydantic models that map to Odoo models, Pydantic model
397397
instances can be created from arbitrary odoo model instances by mapping
398398
fields from odoo models to fields defined by the pydantic model.</p>
399-
<p>To ease the mapping, the addon provide 2 utility classes:</p>
400-
<ul class="simple">
401-
<li>Using <tt class="docutils literal">pydantic&gt;2.0</tt>,
402-
<tt class="docutils literal">odoo.addons.pydantic.utils.PydanticOdooBaseModel</tt>:</li>
403-
</ul>
399+
<p>To ease the mapping, the addon provide an utility class (using
400+
<tt class="docutils literal">pydantic&gt;2.0</tt>) <tt class="docutils literal">odoo.addons.pydantic.utils.PydanticOdooBaseModel</tt>:</p>
404401
<pre class="code python literal-block">
405402
<span class="kn">from</span><span class="w"> </span><span class="nn">odoo.addons.pydantic.utils</span><span class="w"> </span><span class="kn">import</span> <span class="n">PydanticOdooBaseModel</span><span class="w">
406403

@@ -412,32 +409,6 @@ <h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
412409
</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="w">
413410
</span> <span class="n">groups</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Group</span><span class="p">]</span> <span class="o">=</span> <span class="n">pydantic</span><span class="o">.</span><span class="n">Field</span><span class="p">(</span><span class="n">alias</span><span class="o">=</span><span class="s2">&quot;groups_id&quot;</span><span class="p">)</span><span class="w">
414411

415-
</span><span class="n">user</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">user</span><span class="w">
416-
</span><span class="n">user_info</span> <span class="o">=</span> <span class="n">UserInfo</span><span class="o">.</span><span class="n">from_orm</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
417-
</pre>
418-
<ul class="simple">
419-
<li>Using <tt class="docutils literal">pydantic&lt;2.0</tt>,
420-
<tt class="docutils literal">odoo.addons.pydantic.utils.GenericOdooGetter</tt>:</li>
421-
</ul>
422-
<pre class="code python literal-block">
423-
<span class="kn">import</span><span class="w"> </span><span class="nn">pydantic</span><span class="w">
424-
</span><span class="kn">from</span><span class="w"> </span><span class="nn">odoo.addons.pydantic</span><span class="w"> </span><span class="kn">import</span> <span class="n">utils</span><span class="w">
425-
426-
</span><span class="k">class</span><span class="w"> </span><span class="nc">Group</span><span class="p">(</span><span class="n">pydantic</span><span class="o">.</span><span class="n">BaseModel</span><span class="p">):</span><span class="w">
427-
</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="w">
428-
429-
</span> <span class="k">class</span><span class="w"> </span><span class="nc">Config</span><span class="p">:</span><span class="w">
430-
</span> <span class="n">orm_mode</span> <span class="o">=</span> <span class="kc">True</span><span class="w">
431-
</span> <span class="n">getter_dict</span> <span class="o">=</span> <span class="n">utils</span><span class="o">.</span><span class="n">GenericOdooGetter</span><span class="w">
432-
433-
</span><span class="k">class</span><span class="w"> </span><span class="nc">UserInfo</span><span class="p">(</span><span class="n">pydantic</span><span class="o">.</span><span class="n">BaseModel</span><span class="p">):</span><span class="w">
434-
</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="w">
435-
</span> <span class="n">groups</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Group</span><span class="p">]</span> <span class="o">=</span> <span class="n">pydantic</span><span class="o">.</span><span class="n">Field</span><span class="p">(</span><span class="n">alias</span><span class="o">=</span><span class="s2">&quot;groups_id&quot;</span><span class="p">)</span><span class="w">
436-
437-
</span> <span class="k">class</span><span class="w"> </span><span class="nc">Config</span><span class="p">:</span><span class="w">
438-
</span> <span class="n">orm_mode</span> <span class="o">=</span> <span class="kc">True</span><span class="w">
439-
</span> <span class="n">getter_dict</span> <span class="o">=</span> <span class="n">utils</span><span class="o">.</span><span class="n">GenericOdooGetter</span><span class="w">
440-
441412
</span><span class="n">user</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">user</span><span class="w">
442413
</span><span class="n">user_info</span> <span class="o">=</span> <span class="n">UserInfo</span><span class="o">.</span><span class="n">from_orm</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
443414
</pre>

pydantic/tests/test_pydantic_generic_odoo_getter.py

Lines changed: 17 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,11 @@
11
import datetime
2-
from unittest import skipIf
32

43
from odoo import fields
54
from odoo.tests import TransactionCase
65

7-
from pydantic import BaseModel, Field
6+
from pydantic import Field
87

9-
from ..utils import PYDANTIC_V2
10-
11-
if PYDANTIC_V2:
12-
from ..utils import PydanticOdooBaseModel as PydanticOrmBaseModel
13-
14-
else:
15-
from ..utils import GenericOdooGetter
16-
17-
class PydanticOrmBaseModel(BaseModel):
18-
class Config:
19-
orm_mode = True
20-
getter_dict = GenericOdooGetter
8+
from ..utils import PydanticOdooBaseModel as PydanticOrmBaseModel
219

2210

2311
class OdooBaseModel(PydanticOrmBaseModel):
@@ -60,80 +48,6 @@ def setUpClass(cls):
6048
cls.user_demo.share = False
6149

6250

63-
@skipIf(PYDANTIC_V2, "Ignore because Pydantic >= 2.0 is installed")
64-
class TestGenericOdooGetterPydanticV1Case(CommonPydanticCase):
65-
def test_user_model_serialization(self):
66-
self.user_demo.partner_id.date = None
67-
self.assertEqual(
68-
UserModel.from_orm(self.user_demo).dict(),
69-
{
70-
"id": self.user_demo.id,
71-
"partner": {
72-
"id": self.user_demo.partner_id.id,
73-
"name": self.user_demo.partner_id.name,
74-
"date": None,
75-
},
76-
},
77-
)
78-
79-
def test_user_model_serialization_date(self):
80-
self.user_demo.partner_id.date = fields.Date.today()
81-
self.assertEqual(
82-
UserModel.from_orm(self.user_demo).partner.date,
83-
self.user_demo.partner_id.date,
84-
)
85-
86-
def test_user_model_details_serialization_datetime(self):
87-
user_demo = self.user_demo.with_context(tz="Asia/Tokyo")
88-
self.assertEqual(
89-
UserDetailsModel.from_orm(user_demo).write_date,
90-
fields.Datetime.context_timestamp(user_demo, user_demo.write_date),
91-
)
92-
self.assertNotEqual(
93-
UserDetailsModel.from_orm(user_demo).write_date.tzinfo,
94-
fields.Datetime.context_timestamp(
95-
self.user_demo, user_demo.write_date
96-
).tzinfo,
97-
)
98-
99-
def test_user_details_model_serialization(self):
100-
self.assertEqual(
101-
UserDetailsModel.from_orm(self.user_demo).dict(),
102-
{
103-
"id": self.user_demo.id,
104-
"partner": {
105-
"id": self.user_demo.partner_id.id,
106-
"name": self.user_demo.partner_id.name,
107-
"date": None,
108-
},
109-
"groups": [
110-
{
111-
"id": group.id,
112-
"name": group.name,
113-
}
114-
for group in self.user_demo.groups_id
115-
],
116-
"action_id": None,
117-
"signature": None,
118-
"active": True,
119-
"share": False,
120-
"write_date": fields.Datetime.context_timestamp(
121-
self.user_demo, self.user_demo.write_date
122-
),
123-
},
124-
)
125-
126-
def test_user_flat_model_serialization(self):
127-
self.assertEqual(
128-
UserFlatModel.from_orm(self.user_demo).dict(),
129-
{
130-
"id": self.user_demo.id,
131-
"partner_id": self.user_demo.partner_id.id,
132-
},
133-
)
134-
135-
136-
@skipIf(not PYDANTIC_V2, "Ignore because Pydantic < 2.0 is installed")
13751
class TestGenericOdooGetterPydanticV2Case(CommonPydanticCase):
13852
def test_user_model_serialization(self):
13953
self.user_demo.partner_id.date = None
@@ -204,3 +118,18 @@ def test_user_flat_model_serialization(self):
204118
"partner_id": self.user_demo.partner_id.id,
205119
},
206120
)
121+
122+
def test_not_an_odoo_record(self):
123+
user = UserDetailsModel(
124+
id=666,
125+
partner_id={"id": 66, "name": "test"},
126+
groups_id=[{"id": 33, "name": "group 1"}],
127+
action_id={"id": 55},
128+
signature=None,
129+
active=True,
130+
share=False,
131+
write_date=fields.Datetime.now(),
132+
)
133+
self.assertEqual(
134+
UserDetailsModel.model_validate(user).model_dump(), user.model_dump()
135+
)

0 commit comments

Comments
 (0)