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] Future annotations breaks List[Link] #1069

Open
dartt0n opened this issue Nov 17, 2024 · 3 comments
Open

[BUG] Future annotations breaks List[Link] #1069

dartt0n opened this issue Nov 17, 2024 · 3 comments

Comments

@dartt0n
Copy link

dartt0n commented Nov 17, 2024

Describe the bug
When file with models contain from __future__ import annotations Link does not works as expected. Link nests a document instead of creating DbRef.

Reproducible example:

# models.py
from __future__ import annotations

from beanie import Document, Link
from pydantic import UUID4


class UserProfile(Document):
    projects: list[Link["Project"]]
    legacy_id: UUID4

    class Settings:
        name = "user_profile"


class Project(Document):
    user: Link["UserProfile"]
    legacy_id: UUID4

    class Settings:
        name = "project"
# main.py
import asyncio
from uuid import UUID, uuid4

import beanie
from motor.motor_asyncio import AsyncIOMotorClient

import models

users = [
    {"id": str(uuid4())},
    {"id": str(uuid4())},
    {"id": str(uuid4())},
]

projects = [
    {"id": str(uuid4()), "owner_id": users[0]["id"]},
    {"id": str(uuid4()), "owner_id": users[0]["id"]},
    {"id": str(uuid4()), "owner_id": users[1]["id"]},
    {"id": str(uuid4()), "owner_id": users[1]["id"]},
    {"id": str(uuid4()), "owner_id": users[2]["id"]},
    {"id": str(uuid4()), "owner_id": users[2]["id"]},
]


async def load_users() -> list[dict]:
    return users


async def load_projects() -> list[dict]:
    return projects


async def main():
    client = AsyncIOMotorClient("mongodb://localhost:27017")
    await beanie.init_beanie(client.test, document_models=[models.UserProfile, models.Project])

    for data in await load_users():
        user = models.UserProfile(
            projects=[],
            legacy_id=UUID(data["id"]),
        )
        await user.insert()

    for data in await load_projects():
        user = await models.UserProfile.find_one({"legacy_id": UUID(data["owner_id"])})
        assert user is not None

        project = models.Project(
            user=user.to_ref(),
            legacy_id=UUID(data["id"]),
        )
        await project.insert()

    for project in await models.Project.find_all().to_list():
        user = await models.UserProfile.find_one({"_id": project.user.ref.id})
        assert user is not None

        user.projects.append(project)
        await user.save()

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

If I remove from __future__ import annotations line everything works as expected.

Expected behavior
It is expected that projects are saved as DbRef, meanwhile current behaviour clones document and nests it to the parent document. from __future__ import annotations should not have any effect on behaviour.

@dartt0n dartt0n changed the title [BUG] Inconsistent behaviour of List[Link] [BUG] Future annotations breaks List[Link] Nov 25, 2024
Copy link
Contributor

This issue is stale because it has been open 30 days with no activity.

@github-actions github-actions bot added the Stale label Dec 26, 2024
@dartt0n
Copy link
Author

dartt0n commented Jan 7, 2025

This problem is becoming more relevant, as in python 3.14 this behavior of FORWARDREF will become the default behavior. The bug is still present in the latest (1.29.0) release

@dartt0n
Copy link
Author

dartt0n commented Jan 7, 2025

Projects are nested inside user profiles (data duplication) instead of using DbRef
image
image

@github-actions github-actions bot removed the Stale label Jan 8, 2025
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