Timestamp Mixin for auto-populated created_at, updated_at
Discussed in https://github.com/roman-right/beanie/discussions/514
Originally posted by slavovthinks March 25, 2023 Hey, folks 👋 Here is a Timestamp Mixin I've created for Beanie Documents. I'm wondering should I create a PR for it to be included as part of the project itself 🤔
from typing import Optional
from datetime import datetime
from pydantic import BaseModel, Field
from beanie import Document, before_event, Insert, Update, SaveChanges, Replace
class TimestampMixin(BaseModel):
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: Optional[datetime] = None
@before_event(Insert)
def set_created_at(self):
self.created_at = datetime.utcnow()
@before_event(Update, SaveChanges, Replace)
def set_updated_at(self):
self.updated_at = datetime.utcnow()
class ExampleDocument(TimestampMixin, Document):
...
```</div>
This stopped working recently
The created_at is working for me, but not the updated_at
After adding a few logs in the hook, I see that the value of updated_at is getting set correctly, however it's not being updated to the database!
I'm using this
from datetime import datetime
from typing import Any
from beanie import Document, Insert, Replace, SaveChanges, Update, WriteRules, before_event
from pydantic import Field
from pymongo.client_session import ClientSession
class BaseDocument(Document):
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)
@before_event(Insert)
def set_created_at(self) -> None:
self.created_at = datetime.utcnow()
@before_event(Update, SaveChanges, Replace)
def set_updated_at(self) -> None:
self.updated_at = datetime.utcnow()
async def save(
self,
session: ClientSession | None = None,
link_rule: WriteRules = WriteRules.DO_NOTHING,
ignore_revision: bool = False,
**kwargs: Any
) -> None:
self.updated_at = datetime.utcnow()
return await super().save(session, link_rule, ignore_revision, **kwargs)
Overriding the save was because before_event save wasnt reliable
Instead of .save() I used .insert() and it seems to work that way!
Example:
new_user = User(...) # Like invoking the constructor of the model class
new_user.insert()
new_user.fetch_all_links() # (optional)
I modified what @roman-right did, added Save event as well and doc.save() updates the date. Without that the date was not updated as indicated by @humbertogontijo. But i didnt have to ovverride the save method.
@before_event(Update, SaveChanges, Save, Replace)
def set_updated_at(self):
self.updated_at = naive_utcnow()