odmantic
odmantic copied to clipboard
Model.update not detecting datetime change in FastAPI
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.
common cases. see: https://pydantic-docs.helpmanual.io/usage/validators/#validate-always
@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.
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
This is ok
class User(Model):
username: str = Field(index=True)
created: datetime = Field(default=datetime.now)
modified: datetime = Field(default=datetime.now)