Skip to content

Commit

Permalink
fix: pydantic v2 pydantic_model_creator nullable field not optional. (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
waketzheng committed Aug 25, 2023
1 parent 0cb82bb commit ba593c3
Show file tree
Hide file tree
Showing 6 changed files with 22 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Added

Fixed
^^^^^
- Fix pydantic v2 pydantic_model_creator nullable field not optional. (#1454)
- Fix foreign key constraint not generated on MSSQL Server. (#1400)
- Fix testcase error with python3.11 (#1308)

Expand Down
4 changes: 4 additions & 0 deletions examples/blacksheep/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import tortoise
from tortoise import fields, models
from tortoise.contrib.pydantic import pydantic_model_creator

if tortoise.__version__ >= "0.20":
raise RuntimeError("blacksheep not support pydantic V2, use tortoise-orm<0.20 instead!")


class Users(models.Model):
id = fields.UUIDField(pk=True)
Expand Down
4 changes: 2 additions & 2 deletions examples/fastapi/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ async def get_users():

@app.post("/users", response_model=User_Pydantic)
async def create_user(user: UserIn_Pydantic):
user_obj = await Users.create(**user.dict(exclude_unset=True))
user_obj = await Users.create(**user.model_dump(exclude_unset=True))
return await User_Pydantic.from_tortoise_orm(user_obj)


Expand All @@ -33,7 +33,7 @@ async def get_user(user_id: int):

@app.put("/user/{user_id}", response_model=User_Pydantic)
async def update_user(user_id: int, user: UserIn_Pydantic):
await Users.filter(id=user_id).update(**user.dict(exclude_unset=True))
await Users.filter(id=user_id).update(**user.model_dump(exclude_unset=True))
return await User_Pydantic.from_queryset_single(Users.get(id=user_id))


Expand Down
4 changes: 2 additions & 2 deletions examples/pydantic/tutorial_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Creating a Pydantic model from a Tortoise model
* Docstrings & doc-comments are used
* Evaluating the generated schema
* Simple serialisation with both .dict() and .json()
* Simple serialisation with both .model_dump() and .json()
"""
from tortoise import Tortoise, fields, run_async
from tortoise.contrib.pydantic import pydantic_model_creator
Expand Down Expand Up @@ -38,7 +38,7 @@ async def run():
tourpy = await Tournament_Pydantic.from_tortoise_orm(tournament)

# As Python dict with Python objects (e.g. datetime)
print(tourpy.dict())
print(tourpy.model_dump())
# As serialised JSON (e.g. datetime is ISO8601 string representation)
print(tourpy.json(indent=4))

Expand Down
2 changes: 1 addition & 1 deletion examples/pydantic/tutorial_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ async def run():

# As Python dict with Python objects (e.g. datetime)
# Note that the root element is '__root__' that contains the root element.
print(tourpy.dict())
print(tourpy.model_dump())
# As serialised JSON (e.g. datetime is ISO8601 string representation)
print(tourpy.json(indent=4))

Expand Down
13 changes: 12 additions & 1 deletion tortoise/contrib/pydantic/creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from tortoise.contrib.pydantic.base import PydanticListModel, PydanticModel
from tortoise.contrib.pydantic.utils import get_annotations
from tortoise.fields import JSONField, relational
from tortoise.fields import IntField, JSONField, TextField, relational

if TYPE_CHECKING: # pragma: nocoverage
from tortoise.models import Model
Expand Down Expand Up @@ -348,11 +348,13 @@ def get_submodel(_model: "Type[Model]") -> Optional[Type[PydanticModel]]:
return pmodel

# Foreign keys and OneToOne fields are embedded schemas
is_to_one_relation = False
if (
field_type is relational.ForeignKeyFieldInstance
or field_type is relational.OneToOneFieldInstance
or field_type is relational.BackwardOneToOneRelation
):
is_to_one_relation = True
model = get_submodel(fdesc["python_type"])
if model:
if fdesc.get("nullable"):
Expand Down Expand Up @@ -410,6 +412,15 @@ def get_submodel(_model: "Type[Model]") -> Optional[Type[PydanticModel]]:
if field_default is not None and not callable(field_default):
properties[fname] = (ftype, Field(default=field_default, **fconfig))
else:
if (j := fconfig.get("json_schema_extra")) and (
(
j.get("nullable")
and not is_to_one_relation
and field_type not in (IntField, TextField)
)
or (exclude_readonly and j.get("readOnly"))
):
fconfig["default_factory"] = lambda: None
properties[fname] = (ftype, Field(**fconfig))

# Here we endure that the name is unique, but complete objects are still labeled verbatim
Expand Down

0 comments on commit ba593c3

Please sign in to comment.