odmantic icon indicating copy to clipboard operation
odmantic copied to clipboard

Model.update not detecting datetime change in FastAPI

Open M4rk3tt0 opened this issue 1 year ago • 4 comments

Bug

I'm trying to automatically update the modified_at field in my Server model from a FastAPI enpoint, but the Model.update method doesn't update the field.

Current Behavior

For this i have the base model to create a Server (simplified version):

class Server(Model):
    id: UUID = Field(primary_field=True)
    name: str
    created_at: datetime = Field(default_factory=datetime.now)
    modified_at: datetime = Field(default_factory=datetime.now)

and a model used to patch the Server that automatically updates the modified_at field (simplified version):

class ServerUpdate(BaseModel):
    name: Optional[str]
    modified_at: Optional[datetime]

    class Config:
        validate_assignment = True

    @root_validator
    def number_validator(cls, values):
        values["modified_at"] = datetime.now()
        return values

In my FastAPI endpoint i have something like this:

@router.put("/{id}", response_model=Server)
async def update_server_by_id(id: UUID, patch: ServerUpdate):
    server = await engine.find_one(Server, Server.id == id)

    if server is None:
        raise HTTPException(404)

    server.update(patch)

    try:
        await engine.save(server)
        return server
    except Exception:
        raise HTTPException(500)

The modified_at field is never updated.

If I print(patch.dict()) it gives me: {'name': None, 'modified_at': datetime.datetime(2022, 9, 11, 11, 4, 6, 319829)}

If I print(server.dict()) before the update it gives me: {'id': UUID('c4dfa7ac-d25d-45cb-8bc3-f59e5938a58c'), 'name': 'Server 1', 'created_at': datetime.datetime(2022, 9, 11, 9, 46, 45, 234000), 'modified_at': datetime.datetime(2022, 9, 11, 9, 46, 45, 234000)}

so the patch instance has the current datetime.

The only way I found to make it work is to set patch.modified_at = patch.modified_at before server.update(patch)

Expected behavior

The Model.update method should update the datetime field.

Environment

  • ODMantic version: 0.8.0
  • MongoDB version: 5.0.12
  • Pydantic infos (output of python -c "import pydantic.utils; print(pydantic.utils.version_info())):
             pydantic version: 1.10.1
            pydantic compiled: True
                 install path: D:\Projects\Python\vendor-system-fastapi\.venv\Lib\site-packages\pydantic
               python version: 3.10.6 (tags/v3.10.6:9c7b4bd, Aug  1 2022, 21:53:49) [MSC v.1932 64 bit (AMD64)]
                     platform: Windows-10-10.0.19043-SP0
     optional deps. installed: ['dotenv', 'typing-extensions']
  • Version of additional modules (if relevant):
    • fastapi: 0.82.0

Additional context

Add any other context about the problem here.

M4rk3tt0 avatar Sep 11 '22 09:09 M4rk3tt0

common cases. see: https://pydantic-docs.helpmanual.io/usage/validators/#validate-always

linpan avatar Sep 15 '22 02:09 linpan

@linpan I tried your suggestion but the result is the same. It must be something inside the engine save function.

common cases. see: https://pydantic-docs.helpmanual.io/usage/validators/#validate-always

The patch variable has the right datetime value already, but it doesn't get saved by the engine.

M4rk3tt0 avatar Sep 15 '22 05:09 M4rk3tt0

common cases. see: https://pydantic-docs.helpmanual.io/usage/validators/#validate-always

class User(Model):
    username: str = Field(index=True)
    created: datetime = Field(default_factory=datetime.now)
    modified: datetime = Field(default_factory=datetime.now)

    @validator("modified", always=True, pre=True)
    def auto_now(cls, value):
        return datetime.now()

Same

guesswh0 avatar Nov 07 '22 10:11 guesswh0

This is ok

class User(Model):
    username: str = Field(index=True)
    created: datetime = Field(default=datetime.now)
    modified: datetime = Field(default=datetime.now)

InfernalAzazel avatar Nov 11 '22 05:11 InfernalAzazel