tortoise / tortoise-orm

Familiar asyncio ORM for python, built with relations in mind
https://tortoise.github.io
Apache License 2.0
4.58k stars 378 forks source link

fields.TimeDeltaField in pydantic_model_creator raise Input should be None [type=none_required, input_value=datetime.timedelta(second...104, microseconds=48382), input_type=timedelta] #1462

Open yinkh opened 1 year ago

yinkh commented 1 year ago

Describe the bug with tortoise-orm==0.20.0 pydantic==2.2.1

code like:

class Task(Model):
        usage_time = fields.TimeDeltaField(null=True, blank=True, description='usgae')

class TaskListSchema(pydantic_model_creator(Task, name='TaskListSchema', exclude=('usage_time',))):
    pass

TaskListSchema.from_orm(task).dict()

will get error:

Traceback (most recent call last):
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 404, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 78, in __call__
    return await self.app(scope, receive, send)
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\fastapi\applications.py", line 289, in __call__
    await super().__call__(scope, receive, send)
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\starlette\applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\starlette\middleware\errors.py", line 184, in __call__
    raise exc
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\starlette\middleware\errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\starlette\middleware\base.py", line 108, in __call__
    response = await self.dispatch_func(request, call_next)
  File "D:\python_workspace\AutoTestServer\common\middlewares.py", line 18, in dispatch
    response = await call_next(request)
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\starlette\middleware\base.py", line 84, in call_next
    raise app_exc
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\starlette\middleware\base.py", line 70, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\starlette\middleware\cors.py", line 83, in __call__
    await self.app(scope, receive, send)
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\starlette\middleware\exceptions.py", line 79, in __call__
    raise exc
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\starlette\middleware\exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\fastapi\middleware\asyncexitstack.py", line 20, in __call__
    raise e
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\fastapi\middleware\asyncexitstack.py", line 17, in __call__
    await self.app(scope, receive, send)
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\starlette\routing.py", line 718, in __call__
    await route.handle(scope, receive, send)
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\starlette\routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\starlette\routing.py", line 66, in app
    response = await func(request)
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\fastapi\routing.py", line 273, in app
    raw_response = await run_endpoint_function(
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\fastapi\routing.py", line 190, in run_endpoint_function
    return await dependant.call(**values)
  File "D:\python_workspace\AutoTestServer\task\views.py", line 232, in task_list
    data = [TaskListSchema.from_orm(task).model_dump() for task in queryset]
  File "D:\python_workspace\AutoTestServer\task\views.py", line 232, in <listcomp>
    data = [TaskListSchema.from_orm(task).model_dump() for task in queryset]
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\typing_extensions.py", line 2562, in wrapper
    return __arg(*args, **kwargs)
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\pydantic\main.py", line 1066, in from_orm
    return cls.model_validate(obj)
  File "D:\python_workspace\AutoTestServer\venv\lib\site-packages\pydantic\main.py", line 496, in model_validate
    return cls.__pydantic_validator__.validate_python(
pydantic_core._pydantic_core.ValidationError: 1 validation error for TaskListSchema
usage_time
  Input should be None [type=none_required, input_value=datetime.timedelta(second...104, microseconds=48382), input_type=timedelta]
    For further information visit https://errors.pydantic.dev/2.2/v/none_required

To Reproduce print TaskListSchema will get

{
    "additionalProperties": false,
    "properties": {
        "usage_time": {
            "description": "\u4efb\u52a1\u8017\u65f6",
            "nullable": true,
            "title": "Usage Time",
            "type": "null"
        }
    },
    "required": [
        "usage_time"
    ],
    "title": "TaskListSchema",
    "type": "object"
}

when i use tortoise-orm==0.19.2 pydantic==1.10.2

print TaskListSchema will get

{
    "title": "TaskListSchema",
    "description": "\u81ea\u52a8\u5316\u4efb\u52a1",
    "type": "object",
    "properties": {
        "usage_time": {
            "title": "Usage Time",
            "description": "\u4efb\u52a1\u8017\u65f6",
            "nullable": true,
            "type": "number",
            "format": "time-delta"
        }
    },
    "additionalProperties": false
}

this schema will work! Is there something change in pydantic_model_creator when handle fields.TimeDeltaField ? Expected behavior fields.TimeDeltaField in pydantic_model_creator works good

Additional context Add any other context about the problem here.

yinkh commented 1 year ago

pydantic 1.10.12 tortoise-orm 0.19.2 works!

pydantic 1.10.12 tortoise-orm 0.19.3 not works!

long2ice commented 1 year ago

Can you give a full example? I can't reproduce.

yinkh commented 1 year ago

aiomysql 0.2.0
pydantic 1.10.12 tortoise-orm 0.19.3 not work tortoise-orm 0.19.2 work main.py

import asyncio
import datetime

from tortoise import Tortoise
from tortoise.contrib.pydantic import pydantic_model_creator
from tortoise import Model
from tortoise import fields

DATABASE_URI = f"mysql://root:root@127.0.0.1:3306/autotest?charset=utf8&maxsize=100&connect_timeout=30"

class Demo(Model):
    usage_time = fields.TimeDeltaField(null=True, blank=True, description='usage_time')

    class Meta:
        table = "demo"
        ordering = ["-id"]

DemoSchema = pydantic_model_creator(Demo, name='DemoSchema', include=('usage_time',))

async def demo_example():
    print('demo_example')
    await Tortoise.init(db_url=DATABASE_URI, modules={"models": ["__main__"]})
    await Tortoise.generate_schemas()

    start_time = datetime.datetime.now() - datetime.timedelta(hours=1)
    end_time = datetime.datetime.now()
    usage = end_time - start_time
    demo = await Demo.create(usage_time=usage)
    a = DemoSchema.from_orm(demo).dict()
    print(a)

# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    # Blocking call which returns when the display_date() coroutine is done
    loop.run_until_complete(demo_example())
    loop.close()
yinkh commented 1 year ago

after digging, creator.py: Line 261 model_description value in 0.19.2: 0 19 2 in 0.19.3: 19 3

the reason is TimeDeltaField is no longer extend datetime.timedelta, any idea why delete this extend?: image

yinkh commented 1 year ago

image

yinkh commented 1 year ago

When create pr, i try to run test inside my desktop,but this error is raise, and Contribution Guide not explain for this.: image