Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Inconsistent before_event trigger behavior #1103

Open
paulpage opened this issue Jan 3, 2025 · 0 comments
Open

[BUG] Inconsistent before_event trigger behavior #1103

paulpage opened this issue Jan 3, 2025 · 0 comments

Comments

@paulpage
Copy link

paulpage commented Jan 3, 2025

Describe the bug
I have a before_event annotation to update a field on a document every time it is updated. The behavior the event handling is inconsistent based on what update method I use. I have a full reproduction that shows updating 4 documents in 4 different ways, with differing results:

  1. find_one(...).update(...) - Not triggered, tag field NOT set in db
  2. find_one(...); result.update(...) - Triggered ONCE, tag field NOT set in db
  3. find_one(...); result.set(...) - Triggered ONCE, tag field NOT set in db
  4. find_one(...); result.field = val; result.save() - Triggered TWICE; tag field SET in db

To Reproduce

import asyncio

from beanie import Document, Replace, Save, SaveChanges, Update, before_event
from beanie import init_beanie
from motor.motor_asyncio import AsyncIOMotorClient
from beanie.odm.operators.update.general import Set
from beanie.odm.queries.update import UpdateResponse


class Person(Document):
    name: str
    tag: str | None = None

    @before_event(Save, Update, Replace, SaveChanges)
    def set_tag(self):
        print("    before_event TRIGGERED")
        self.tag = "updated"


async def main():
    client = AsyncIOMotorClient("mongodb://localhost:27017")
    client.get_io_loop = asyncio.get_running_loop
    await init_beanie(
        client["mydb"],
        document_models=[
            Person,
        ],
    )

    print("=== create")
    await Person(name="Alice").insert()
    await Person(name="Bob").insert()
    await Person(name="Charlie").insert()
    await Person(name="Dan").insert()

    print("=== find_one.update")
    result = await Person.find_one(Person.name == "Alice").update(Set({"name": "Alicia"}), response_type=UpdateResponse.NEW_DOCUMENT)
    print(f"    result: {result}")

    print("=== find_one; update")
    result = await Person.find_one(Person.name == "Bob")
    result = await result.update(Set({"name": "Bobby"}))
    print(f"    result: {result}")

    print("=== find_one; set")
    result = await Person.find_one(Person.name == "Charlie")
    result = await result.set({"name": "Charles"})
    print(f"    result: {result}")

    print("=== find_one; save")
    result = await Person.find_one(Person.name == "Dan")
    result.name = "Daniel"
    await result.save()
    print(f"    result: {result}")

    print("=== close")
    client.close()

if __name__ == "__main__":
    asyncio.run(main())

Expected behavior
I'm unsure of whether or not the before_event should be triggered twice during the 4th case (find, update, save()), but for all 4 cases I would expect the before_event to get triggered at least once, and I would expect the final value in the DB to have a "tag": "updated" value.

Additional context
I'm particularly interested in the behavior of the first case, where .update() is called directly on the result of find_one() - I would like to use this pattern with a before_event annotation to automatically set an updatedAt field on my documents.

The repro code can be run with beanie installed and an empty mongodb instance (I used the mongo:5 docker image, but I suspect a local instance would work as well).

Python version: 3.10.10
Beanie version: 1.28.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant