Skip to content

Better understanding create_pydantic_model with foreign keys #1292

@badlydrawnrob

Description

@badlydrawnrob

So I'm having a little trouble understanding create_pydantic_model where foreign keys are concerned — I have a feeling it might be easier (in some cases) to just create your own Pydantic models — perhaps the documentation could be a little more clear and extensive in this area? I imagined the automatic Pydantic models are flexible enough for most queries, but can't figure out how to do it reliably.

See example code here, I've tried:

Flat shape

I'm happy with a flat shape, but how do you create_pydantic_model for it? I think the way I've been trying it is wrong.

Without any pydantic model

fruits = await (
    Fruits.select(
        Fruits.all_columns(exclude=[Fruits.id, Fruits.color]), # Return the fruits table
        Fruits.color.all_columns() # Join on the colors table
    )
)

return fruits

Output without a response_model pydantic type (a list of fruits):

[
  {
    "image": null,
    "name": "Banana",
    "url": "4352d224-b0ce-4639-8696-942b2220fc0a",
    "color.id": 1,
    "color.color": "#cc0f35",
    "color.background": "#feecf0",
    "color.name": "red"
  }
]

What I've tried to do (flat shape)

My first thought was to include_columns=(Fruits.name, Fruits.color, Fruits.color.name, ...) but it seems only Fruits columns are allowed there (not the foreign table fields).

I've also tried a plain model with basic arguments (single result example)

FruitsModelOut: Any = create_pydantic_model(
    table=Fruits,
    model_name="FruitsModelOut",
)

fruits = await (
    Fruits.select(
        Fruits.all_columns(exclude=[Fruits.id, Fruits.color]), # Return the fruits table
        Fruits.color.all_columns() # Join on the colors table
    ).first()
)

FruitsModelOut(**fruits)

Outputs this, with none of the extra fields I'd expect (with color also set to null)

{
  "color": null,
  "image": null,
  "name": "Banana",
  "url": "4352d224-b0ce-4639-8696-942b2220fc0a"
}

Nested shape

This feels a little easier to understand, but I'd prefer a flatter model ...

FruitsAllModelOut: Any = create_pydantic_model(
    table=Fruits,
    model_name="FruitsAllModelOut",
    nested=True,
    include_default_columns=True,
)

@fruits_router.get("/", response_model=List[FruitsAllModelOut])
async def retrieve_all_fruits():
    fruits = await (
        Fruits.select(
            Fruits.all_columns(exclude=[Fruits.id, Fruits.color]), # Return the fruits table
            Fruits.color.all_columns() # Join on the colors table
        ).output(nested=True)
    )

    return fruits

Response (unfortunately it gives null value for id, but you can remove the include_default_columns argument to hide it)

[
  {
    "id": null,
    "color": {
      "id": 1,
      "color": "#cc0f35",
      "background": "#feecf0",
      "name": "red"
    },
    "image": null,
    "name": "Banana",
    "url": "4352d224-b0ce-4639-8696-942b2220fc0a"
  }
]

TL;DR

So yeah, using "magic" at the moment feels a little harder to understand than just creating your own Pydantic models for foreign key fields with a flat shape, or for more complex queries. Perhaps this is the limitation of create_pydantic_types? Or I'm misunderstanding.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions