Include nested and related, Many-to-Many
The same as usual includes. Here’s an example with an association object.
Example (sources here):
Prepare models and schemas
Define SQLAlchemy models
Parent model
models/parent.py
:
from sqlalchemy.orm import Mapped, relationship
from .base import Base
from .parent_to_child_association import ParentToChildAssociation
class Parent(Base):
__tablename__ = "left_table_parents"
name: Mapped[str]
children: Mapped[list[ParentToChildAssociation]] = relationship(back_populates="parent", cascade="delete")
Child model
models/child.py
:
from sqlalchemy.orm import Mapped, relationship
from .base import Base
from .parent_to_child_association import ParentToChildAssociation
class Child(Base):
__tablename__ = "right_table_children"
name: Mapped[str]
parents: Mapped[list[ParentToChildAssociation]] = relationship(back_populates="child", cascade="delete")
Parent to Child Association model
models/parent_child_association.py
:
Define pydantic schemas
Parent Schema
schemas/parent.py
:
Child Schema
schemas/child.py
:
Parent to Child Association Schema
schemas/parent_child_association.py
:
Define view classes
Base Views
api/base.py
:
from typing import ClassVar
from fastapi import Depends
from pydantic import BaseModel, ConfigDict
from sqlalchemy.engine import make_url
from sqlalchemy.ext.asyncio import AsyncSession
from examples.api_for_sqlalchemy import config
from examples.api_for_sqlalchemy.models.db import DB
from fastapi_jsonapi.data_layers.sqla.orm import SqlalchemyDataLayer
from fastapi_jsonapi.misc.sqla.generics.base import ViewBaseGeneric
from fastapi_jsonapi.views import Operation, OperationConfig, ViewBase
db = DB(
url=make_url(config.SQLA_URI),
)
class SessionDependency(BaseModel):
model_config = ConfigDict(
arbitrary_types_allowed=True,
)
session: AsyncSession = Depends(db.session)
def handler(view: ViewBase, dto: SessionDependency) -> dict:
return {
"session": dto.session,
}
class ViewBase(ViewBaseGeneric):
"""
Generic view base (detail)
"""
data_layer_cls = SqlalchemyDataLayer
operation_dependencies: ClassVar = {
Operation.ALL: OperationConfig(
dependencies=SessionDependency,
prepare_data_layer_kwargs=handler,
),
}
List Parent objects with Children through an Association object
Request:
GET /parents?include=children%2Cchildren.child HTTP/1.1
Content-Type: application/vnd.api+json
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": [
{
"attributes": {
"name": "parent_1"
},
"id": "1",
"relationships": {
"children": {
"data": [
{
"id": "1",
"type": "parent_child_association"
},
{
"id": "3",
"type": "parent_child_association"
}
]
}
},
"type": "parent"
},
{
"attributes": {
"name": "parent_2"
},
"id": "2",
"relationships": {
"children": {
"data": [
{
"id": "2",
"type": "parent_child_association"
},
{
"id": "4",
"type": "parent_child_association"
},
{
"id": "5",
"type": "parent_child_association"
}
]
}
},
"type": "parent"
},
{
"attributes": {
"name": "parent_3"
},
"id": "3",
"relationships": {
"children": {
"data": []
}
},
"type": "parent"
}
],
"included": [
{
"attributes": {
"name": "child_1"
},
"id": "1",
"type": "child"
},
{
"attributes": {
"name": "child_2"
},
"id": "2",
"type": "child"
},
{
"attributes": {
"name": "child_3"
},
"id": "3",
"type": "child"
},
{
"attributes": {
"extra_data": "assoc_p1c1_extra"
},
"id": "1",
"relationships": {
"child": {
"data": {
"id": "1",
"type": "child"
}
}
},
"type": "parent_child_association"
},
{
"attributes": {
"extra_data": "assoc_p2c1_extra"
},
"id": "2",
"relationships": {
"child": {
"data": {
"id": "1",
"type": "child"
}
}
},
"type": "parent_child_association"
},
{
"attributes": {
"extra_data": "assoc_p1c2_extra"
},
"id": "3",
"relationships": {
"child": {
"data": {
"id": "2",
"type": "child"
}
}
},
"type": "parent_child_association"
},
{
"attributes": {
"extra_data": "assoc_p2c2_extra"
},
"id": "4",
"relationships": {
"child": {
"data": {
"id": "2",
"type": "child"
}
}
},
"type": "parent_child_association"
},
{
"attributes": {
"extra_data": "assoc_p2c3_extra"
},
"id": "5",
"relationships": {
"child": {
"data": {
"id": "3",
"type": "child"
}
}
},
"type": "parent_child_association"
}
],
"jsonapi": {
"version": "1.0"
},
"meta": {
"count": 3,
"totalPages": 1
}
}