0

I've got 3 database models; Graphs, Nodes and Paths. Graphs have nodes and nodes have paths and paths have destination and source nodes. Here is the models (I am using ormar ORM):

class Graph(BaseModel):
    """Model for Graph objects."""

    class Meta(BaseMeta):
        tablename = "graphs"

    id: int = ormar.BigInteger(primary_key=True)
    name: str = ormar.String(max_length=MAX_NAME_SIZE, unique=True)
    code: str = ormar.String(max_length=MAX_CODE_SIZE, unique=True)


class Node(BaseModel):
    """Model for Node objects."""

    class Meta(BaseMeta):
        tablename = "nodes"

    id: int = ormar.BigInteger(primary_key=True)
    header: str = ormar.String(max_length=MAX_CHAR_VAR_SIZE)
    body: str = ormar.Text(nullable=True)

    graph: Graph = ormar.ForeignKey(
        Graph,
        nullable=False,
        ondelete="CASCADE",
    )

class Path(BaseModel):
    """Model for Path objects."""

    class Meta(BaseMeta):
        tablename = "paths"

    id: int = ormar.BigInteger(primary_key=True)
    kind: str = ormar.Text(nullable=False)
    context: str = ormar.Text(nullable=False)

    source: Node = ormar.ForeignKey(
        Node,
        nullable=False,
        ondelete="CASCADE",
        related_name="source_paths",
    )

    destination: Node = ormar.ForeignKey(
        Node,
        nullable=False,
        ondelete="CASCADE",
        related_name="destination_paths",
    )

What I want to do is create an endpoint that response the graph data with nested node and path information like this:

{
  "id": 28,
  "name": "test",
  "code": "abc",
  "nodes": [
    {
      "id": 18,
      "header": "lorem",
      "body": "ipsum",
      "paths": [
        {
          "destination_id": 19,
          "kind": "SELECT",
          "context": "SOME TEXT"
        }
      ]
    }
}

I have queried the Graph object like this:

await Graph.objects.select_related("nodes__destination_paths__destination").get(id=graph_id)

and have some pydantic models to create response:

class PathResponse(BaseModel):
    destination_id: int
    kind: PathKindEnum
    context: str

    @root_validator
    def set_destination_id(cls, values):
        # values = {'kind': '...', 'context': 'testing'}
        values["destination_id"] = values["destination"].id

    class Config:
        orm_mode = True
        allow_population_by_field_name = True
        use_enum_values = True


class NodeResponse(BaseModel):
    id: int
    header: str
    body: str
    paths: list[PathResponse] = []

    class Config:
        orm_mode = True
        allow_population_by_field_name = True
        use_enum_values = True
        fields = {"paths": {"alias": "destination_paths"}}


class GraphResponse(BaseModel):
    id: int
    name: str
    code: str
    nodes: list[NodeResponse] = []

    class Config:
        orm_mode = True
        allow_population_by_field_name = True
        use_enum_values = True
        fields = {"nodes": {"alias": "graph_nodes"}}

When I make the request, I got the error:

File "/app_path/web/api/graphs/schema.py", line 69, in set_destination_id
    values["destination_id"] = values["destination"].id
    │                          └ {'kind': 'BUTTON', 'context': 'testing'}
    └ {'kind': 'BUTTON', 'context': 'testing'}

KeyError: 'destination'

destination object is missing in the dict but I have no idea how to add this information. I think my query can be wrong so I've also try

return await Graph.objects.select_related(
            ["nodes", "nodes__source_paths", "nodes__destination_paths"],
        ).get(id=graph_id)

but that did not work either. What am I missing here?

mdegis
  • 2,078
  • 2
  • 21
  • 39

0 Answers0