Skip to content

Commit

Permalink
upgrade to new fastapi and pydantic (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
mahenzon committed Dec 19, 2023
1 parent e6224e9 commit 1195419
Show file tree
Hide file tree
Showing 18 changed files with 1,377 additions and 1,333 deletions.
12 changes: 6 additions & 6 deletions examples/api_for_tortoise_orm/api/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ async def get_user(cls, user_id: int, query_params: QueryStringManager) -> User:
@classmethod
async def get(cls, obj_id: int, query_params: QueryStringManager) -> UserSchema:
user: User = await cls.get_user(user_id=obj_id, query_params=query_params)
return UserSchema.from_orm(user)
return UserSchema.model_validate(user)

@classmethod
async def patch(cls, obj_id: int, data: UserPatchSchema, query_params: QueryStringManager) -> UserSchema:
Expand All @@ -64,7 +64,7 @@ async def patch(cls, obj_id: int, data: UserPatchSchema, query_params: QueryStri
except ObjectNotFound as ex:
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail=ex.description)

user = UserSchema.from_orm(user_obj)
user = UserSchema.model_validate(user_obj)
return user


Expand All @@ -75,22 +75,22 @@ async def get(cls, query_params: QueryStringManager) -> Union[QuerySet, JSONAPIR
dl = TortoiseDataLayer(query=user_query, schema=UserSchema, model=User)
count, users_db = await dl.get_collection(qs=query_params)
total_pages = count // query_params.pagination.size + (count % query_params.pagination.size and 1)
users: List[UserSchema] = [UserSchema.from_orm(i_user) for i_user in users_db]
users: List[UserSchema] = [UserSchema.model_validate(i_user) for i_user in users_db]
return JSONAPIResultListSchema(
meta={"count": count, "totalPages": total_pages},
data=[{"id": i_obj.id, "attributes": i_obj.dict(), "type": "user"} for i_obj in users],
data=[{"id": i_obj.id, "attributes": i_obj.model_dump(), "type": "user"} for i_obj in users],
)

@classmethod
async def post(cls, data: UserInSchema, query_params: QueryStringManager) -> UserSchema:
try:
user_obj = await UserFactory.create(
data=data.dict(),
data=data.model_dump(),
mode=FactoryUseMode.production,
header=query_params.headers,
)
except ErrorCreateUserObject as ex:
raise BadRequest(ex.description, ex.field)

user = UserSchema.from_orm(user_obj)
user = UserSchema.model_validate(user_obj)
return user
2 changes: 1 addition & 1 deletion fastapi_jsonapi/atomic/atomic_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ async def wrapper(*a, operation: OperationBase, **kw):
errors_details = {
"message": f"Validation error on operation {operation.op_type}",
"ref": operation.ref,
"data": operation.data.dict(),
"data": operation.data.model_dump(),
}
if isinstance(ex, ValidationError):
errors_details.update(errors=ex.errors())
Expand Down
37 changes: 28 additions & 9 deletions fastapi_jsonapi/atomic/schemas.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

from enum import Enum
from typing import List, Optional, Union
from typing import Annotated, List, Optional, Union

from pydantic import BaseModel, Field, root_validator
from starlette.datastructures import URLPath
Expand Down Expand Up @@ -177,10 +177,21 @@ def validate_operation(cls, values: dict):


class AtomicOperationRequest(BaseModel):
operations: List[AtomicOperation] = Field(
alias="atomic:operations",
min_items=1,
)
# operations: conlist(AtomicOperation, min_length=1)
# operations: conlist(AtomicOperation, min_length=1)
operations: Annotated[
List[AtomicOperation],
Field(alias="atomic:operations"),
]

# operations: conlist(AtomicOperation, min_length=1) = Field(
# alias="atomic:operations",
# )
# operations: List[AtomicOperation] = Field(
# alias="atomic:operations",
# # TODO: how to limit?
# # min_length=1,
# )


class AtomicResult(BaseModel):
Expand All @@ -199,7 +210,15 @@ class AtomicResultResponse(BaseModel):
https://jsonapi.org/ext/atomic/#auto-id-responses-4
"""

results: List[AtomicResult] = Field(
alias="atomic:results",
min_items=1,
)
results: Annotated[
List[AtomicResult],
Field(
alias="atomic:results",
# min_length=1,
),
]
# results: List[AtomicResult] = Field(
# alias="atomic:results",
# # TODO: how to limit?
# min_length=1,
# )
2 changes: 1 addition & 1 deletion fastapi_jsonapi/exceptions/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ExceptionSchema(BaseModel):
status: str
source: Optional[ExceptionSourceSchema] = None
title: str
detail: Any
detail: Any = None


class ExceptionResponseSchema(BaseModel):
Expand Down
27 changes: 16 additions & 11 deletions fastapi_jsonapi/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@

from fastapi import FastAPI
from pydantic import (
BaseConfig,
BaseModel,
Extra,
ConfigDict,
Field,
)

Expand All @@ -25,12 +24,13 @@


class BaseJSONAPIRelationshipSchema(BaseModel):
model_config = ConfigDict(
extra="forbid",
)

id: str = Field(..., description="Related object ID")
type: str = Field(..., description="Type of the related resource object")

class Config(BaseConfig):
extra = Extra.forbid


class BaseJSONAPIRelationshipDataToOneSchema(BaseModel):
data: BaseJSONAPIRelationshipSchema
Expand All @@ -56,7 +56,7 @@ class BaseJSONAPIItemInSchema(BaseJSONAPIItemSchema):

attributes: "TypeSchema" = Field(description="Resource object attributes")
relationships: Optional["TypeSchema"] = Field(None, description="Resource object relationships")
id: Optional[str] = Field(description="Resource object ID")
id: Optional[str] = Field(None, description="Resource object ID")


class BaseJSONAPIDataInSchema(BaseModel):
Expand All @@ -72,11 +72,16 @@ class BaseJSONAPIObjectSchema(BaseJSONAPIItemSchema):
class JSONAPIResultListMetaSchema(BaseModel):
"""JSON:API list meta schema."""

count: Optional[int]
total_pages: Optional[int] = Field(alias="totalPages")
model_config = ConfigDict(
extra="forbid",
populate_by_name=True,
)

class Config:
allow_population_by_field_name = True
count: Optional[int] = None
total_pages: Optional[int] = Field(
None,
serialization_alias="totalPages",
)


class JSONAPIDocumentObjectSchema(BaseModel):
Expand All @@ -97,7 +102,7 @@ class BaseJSONAPIResultSchema(BaseModel):
JSON:API Required fields schema
"""

meta: Optional[JSONAPIResultListMetaSchema] = Field(description="JSON:API metadata")
meta: Optional[JSONAPIResultListMetaSchema] = Field(None, description="JSON:API metadata")
jsonapi: JSONAPIDocumentObjectSchema = JSONAPIDocumentObjectSchema()


Expand Down
9 changes: 5 additions & 4 deletions fastapi_jsonapi/schema_base.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from pydantic import ConfigDict

__all__ = (
"Field",
"BaseModel",
Expand Down Expand Up @@ -43,13 +45,12 @@ class BaseModel(BaseModelGeneric, metaclass=RegistryMeta):


class RelationshipInfo(BaseModel):
model_config = ConfigDict(
frozen=True,
)
resource_type: str
many: bool = False
related_view: str = None
related_view_kwargs: Dict[str, str] = {}
resource_id_example: str = "1"
id_field_name: str = "id"

# TODO: Pydantic V2 use model_config
class Config:
frozen = True
10 changes: 5 additions & 5 deletions fastapi_jsonapi/views/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from functools import cache
from typing import Callable, Coroutine, Optional, Set, Type, Union

from pydantic import BaseModel
from pydantic import BaseModel, ConfigDict


class HTTPMethod(Enum):
Expand All @@ -18,11 +18,11 @@ def names() -> Set[str]:


class HTTPMethodConfig(BaseModel):
model_config = ConfigDict(
arbitrary_types_allowed=True,
)
dependencies: Optional[Type[BaseModel]] = None
prepare_data_layer_kwargs: Optional[Union[Callable, Coroutine]] = None

class Config:
arbitrary_types_allowed = True
prepare_data_layer_kwargs: Optional[Callable] = None

@property
def handler(self) -> Optional[Union[Callable, Coroutine]]:
Expand Down
9 changes: 6 additions & 3 deletions fastapi_jsonapi/views/view_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,10 @@ def _build_list_response(self, items_from_db: List[TypeModel], count: int, total
includes_schemas=object_schemas.included_schemas_list,
)
return list_jsonapi_schema(
meta=JSONAPIResultListMetaSchema(count=count, total_pages=total_pages),
meta=JSONAPIResultListMetaSchema(
count=count,
total_pages=total_pages,
),
data=result_objects,
**extras,
)
Expand Down Expand Up @@ -259,7 +262,7 @@ def update_related_object(
if hasattr(parent_included_object, "relationships") and parent_included_object.relationships:
existing = parent_included_object.relationships or {}
if isinstance(existing, BaseModel):
existing = existing.dict()
existing = existing.model_dump()
new_relationships.update(existing)
new_relationships.update(
**{
Expand Down Expand Up @@ -417,7 +420,7 @@ def process_db_object(

item_as_schema = object_schemas.object_jsonapi_schema(
id=self.get_db_item_id(item),
attributes=object_schemas.attributes_schema.from_orm(item),
attributes=object_schemas.attributes_schema.model_validate(item),
)

cache_included_objects: Dict[Tuple[str, str], TypeSchema] = {}
Expand Down
Loading

0 comments on commit 1195419

Please sign in to comment.