odmantic icon indicating copy to clipboard operation
odmantic copied to clipboard

Handling references to external models by `id`

Open bbalet opened this issue 3 years ago • 1 comments

Hi,

I'm working on a FastAPI application with ODMantic and it's fantastic. However, I'm a bit confused by how references to external models are handled. Maybe I did it all wrong, but I came up with the same solution than https://github.com/art049/odmantic/issues/127#issuecomment-887339892 in order to avoid a validation error

Let's say that I'm trying to model a project and its builds:

class Build(Model):
    project: Project = Reference()
    name: str
    date: datetime
    success: bool

I must use an additional model:

class BuildInApi(Model):
    project_id: str
    name: str
    date: datetime
    success: bool

Or project_id: Optional[ObjectId] but it's the same.

And then to:

  1. Find the related project.
  2. Map the fields between the two models

As in this example endpoint:

@router.put("/builds/", response_model=Build, tags=["builds"])
async def create_build(build_in_api: BuildInApi, request: Request):
    project = await request.app.engine.find_one(Project, Project.id == ObjectId(build_in_api.project_id))
    build = Build(project=project, 
                    name=build_in_api.name, 
                    date=build_in_api.date, 
                    success=build_in_api.success, 
                    payload=build_in_api.payload)
    await request.app.engine.save(build)
    return build

This is cumbersome and errorprone so I'm sure I am missing something.

bbalet avatar Jul 31 '21 14:07 bbalet

Hello, thanks for showing this use case. Yeah, currently this is, unfortunately, the only way. Although you could avoid manual unpacking like this:

@router.put("/builds/", response_model=Build, tags=["builds"])
async def create_build(build_in_api: BuildInApi, request: Request):
    project = await request.app.engine.find_one(Project, Project.id == ObjectId(build_in_api.project_id))
    build = Build(project=project, **build_in_api.dict(exclude="project_id"))
    await request.app.engine.save(build)
    return build

However, it doesn't relieve the pain that much. I'll include this use case in the reference rework coming soon!

art049 avatar Sep 25 '22 16:09 art049