Skip to content

Commit

Permalink
Merge PR #380 into 16.0
Browse files Browse the repository at this point in the history
Signed-off-by sebastienbeau
  • Loading branch information
OCA-git-bot committed Oct 13, 2023
2 parents dd3f433 + 4b196ef commit 65cb804
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 33 deletions.
2 changes: 1 addition & 1 deletion extendable_fastapi/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"external_dependencies": {
"python": [
"pydantic>=2.0.0",
"extendable-pydantic>=1.1.0",
"extendable-pydantic>=1.2.0",
],
},
"installable": True,
Expand Down
Empty file.
7 changes: 7 additions & 0 deletions extendable_fastapi/readme/newsfragments/380.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
* New base schemas: *PagedCollection*. This schema is used to define the
the structure of a paged collection of resources. This schema is similar
to the ones defined in the Odoo's **fastapi** addon but works as/with
extendable models.

* The *StrictExtendableBaseModel* has been moved to the *extendable_pydantic*
python lib. You should consider to import it from there.
37 changes: 15 additions & 22 deletions extendable_fastapi/schemas.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
from extendable_pydantic import ExtendableBaseModel
from typing import Annotated, Generic, TypeVar

from extendable_pydantic import StrictExtendableBaseModel

class StrictExtendableBaseModel(
ExtendableBaseModel,
revalidate_instances="always",
validate_assignment=True,
extra="forbid",
):
"""
An ExtendableBaseModel with strict validation.
from pydantic import Field

By default, Pydantic does not revalidate instances during validation, nor
when the data is changed. Validation only occurs when the model is created.
This is not suitable for a REST API, where the data is changed after the
model is created or the model is created from a partial set of data and
then updated with more data. This class enforces strict validation by
forcing the revalidation of instances when the method `model_validate` is
called and by ensuring that the values assignment is validated.
T = TypeVar("T")

The following parameters are added:
* revalidate_instances="always": model instances are revalidated during validation
(default is "never")
* validate_assignment=True: revalidate the model when the data is changed (default is False)
* extra="forbid": Forbid any extra attributes (default is "ignore")
"""

class PagedCollection(StrictExtendableBaseModel, Generic[T]):
"""A paged collection of items"""

# This is a generic model. The type of the items is defined by the generic type T.
# It provides you a common way to return a paged collection of items of
# extendable models. It's based on the StrictExtendableBaseModel to ensure
# a strict validation when used within the odoo fastapi framework.

count: Annotated[int, Field(..., description="The count of items into the system")]
items: list[T]
9 changes: 5 additions & 4 deletions fastapi/readme/USAGE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1116,8 +1116,9 @@ the app is robust and easy to maintain. Here are some of them:

* As a corollary of the previous point, a search handler must always use the
pagination mechanism with a reasonable default page size. The result list
must be enclosed in a json document that contains the total number of records
and the list of records.
must be enclosed in a json document that contains the count of records into
the system matching your search criteria and the list of records for the given
page and size.

* Use plural for the name of a service. For example, if you provide a service
that allows you to manage the sale orders, you must use the name 'sale_orders'
Expand Down Expand Up @@ -1149,7 +1150,7 @@ you be consistent when writing a route handler for a search route.
1. A dependency method to use to specify the pagination parameters in the same
way for all the search route handlers: **'odoo.addons.fastapi.paging'**.
2. A PagedCollection pydantic model to use to return the result of a search route
handler enclosed in a json document that contains the total number of records.
handler enclosed in a json document that contains the count of records.

.. code-block:: python
Expand Down Expand Up @@ -1179,7 +1180,7 @@ you be consistent when writing a route handler for a search route.
count = env["sale.order"].search_count([])
orders = env["sale.order"].search([], limit=paging.limit, offset=paging.offset)
return PagedCollection[SaleOrder](
total=count,
count=count,
items=[SaleOrder.model_validate(order) for order in orders],
)
Expand Down
Empty file.
7 changes: 7 additions & 0 deletions fastapi/readme/newsfragments/380.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
The field *total* in the *PagedCollection* schema is replaced by the field *count*.
The field *total* is now deprecated and will be removed in the next major version.
This change is backward compatible. The json document returned will now
contain both fields *total* and *count* with the same value. In your python
code the field *total*, if used, will fill the field *count* with the same
value. You are encouraged to use the field *count* instead of *total* and adapt
your code accordingly.
31 changes: 26 additions & 5 deletions fastapi/schemas.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
# Copyright 2022 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import warnings
from enum import Enum
from typing import Generic, List, Optional, TypeVar
from typing import Annotated, Generic, List, Optional, TypeVar

from pydantic import BaseModel, ConfigDict, Field
from pydantic import AliasChoices, BaseModel, ConfigDict, Field, computed_field

T = TypeVar("T")


class PagedCollection(BaseModel, Generic[T]):

total: int
count: Annotated[
int,
Field(
...,
desciption="Count of items into the system.\n "
"Replaces the total field which is deprecated",
validation_alias=AliasChoices("count", "total"),
),
]
items: List[T]

@computed_field()
@property
def total(self) -> int:
return self.count

@total.setter
def total(self, value: int):
warnings.warn(
"The total field is deprecated, please use count instead",
DeprecationWarning,
stacklevel=2,
)
self.count = value


class Paging(BaseModel):
limit: Optional[int] = None
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ apispec>=4.0.0
cerberus
contextvars
extendable-pydantic
extendable-pydantic>=1.1.0
extendable-pydantic>=1.2.0
extendable>=0.0.4
fastapi
graphene
Expand Down

0 comments on commit 65cb804

Please sign in to comment.