beanie icon indicating copy to clipboard operation
beanie copied to clipboard

Link type fails in context of FastAPI application

Open aknoerig opened this issue 2 years ago • 4 comments

When trying to use the Link type with FastAPI, the mappings from/to JSON fail and/or cause some serious problems. Consider this simple data structure:

class A(Document):
    name: str

    class Collection:
        name: "as"

class B(Document):
    name: str
    a: Link[A]

    class Collection:
        name: "bs"

Some corresponding FastAPI routes and their issues:

POST

@router.post("/b")
async def create_b(b:B = Body(...)):
    inserted_b = await B.insert_one()
    return inserted_b

This requires the post body to look like this:

{ "name": "foo", "a": { "_id": "abc213" } }

but results in a document of the form:

{ "name": "foo", "a": { "$id": "abc213", "$collection":"as" } }

That's ok'ish, but counter-intuitive, and leads to additional problems in the update (see below).

GET

@router.get("/b/{b_id}")
async def get_b(b_id:str):
    b = await B.get(b_id)
    return b

This fails, because the Link field can't be serialized (see issue #199)

PUT

@router.put("/b/{b_id}")
async def update_b(b_id:str, b:B = Body(...)):
    existing = await B.get(b_id)
    await existing.set(b)
    return existing

If you then send it the following body:

{ "name": "bar", "a": { "$id": "abc213", "$collection":"as" } }

..it will actually dereference the link(!), and stores the result to mongo like this:

{ "name": "bar", "a": { "_id": "abc213", "name":"something" } }

This breaks one's collection (and probably without noticing, because test would still pass).

Conclusion

As it stands, the Link type is not usable (or I'm missing something..). A lot of confusion seems to be related to how a Link is represented. Internally it's a DBRef, while on the json side it can be an object with an _id. Sometimes it gets automatically dereferenced into the linked object, sometimes not.

Maybe everything would be easier if the Link does not use DBRef, but just an ObjectId. This is anyway the advised default by MongoDb:

For nearly every case where you want to store a relationship between two documents, use manual references. The references are simple to create and your application can resolve references as needed.

Generally, it seems useful to test & document all beanie functions in the context of a simple FastAPI application. This will uncover issues related to JSON mappings.

aknoerig avatar Feb 08 '22 10:02 aknoerig

Been wanting link type to work with FastAPI so bad

schwannden avatar Mar 15 '22 07:03 schwannden

@roman-right I'd like to potentially help with this issue. Is this something you'd be willing to merge if the solution were acceptable to you?

smeggingsmegger avatar Apr 06 '22 22:04 smeggingsmegger

@aknoerig it also says this though:

The only limitation of manual linking is that these references do not convey the database and collection names. If you have documents in a single collection that relate to documents in more than one collection, you may need to consider using DBRefs.

Sylver11 avatar Jun 03 '22 14:06 Sylver11

Yea manual links would suffice in many cases, but the @roman-right mentioned before In discord that multi-database support is something he considered. So DBRef seems to be ideal for long-term support of the project. I have faced many issues with Links as I try to convert my project over and almost considered just using manual _id refs.

killswitch-GUI avatar Jun 06 '22 16:06 killswitch-GUI

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

github-actions[bot] avatar Feb 13 '23 02:02 github-actions[bot]

This issue was closed because it has been stalled for 14 days with no activity.

github-actions[bot] avatar Feb 27 '23 02:02 github-actions[bot]