I have several model classes, let's say A_0, A_1, ..., A_N
I have a method A_0.to_A_1() that calls A_1.to_A_2(), ... that calls ..., that calls A_(N-1).to_A_N()
So far so good, but A_N may then call again to_A_M() with M < N if A_N is an instance of A_K with K < M.
That's bad! So I came up with this neat solution:
from functools import wraps
import threading
def nrec(func):
thread_local = threading.local()
@wraps(func)
def wrapper(*args, **kwargs):
if getattr(thread_local, '__nrec_visited', False):
return None
thread_local.__nrec_visited = True
result = func(*args, **kwargs)
thread_local.__nrec_visited = False
return result
return wrapper
I think this is so cool, also thread-safe so I can use it with FastAPI and such.
Here's an example:
class OrganizationData(SQLModel, table=True):
__tablename__ = "organization"
__table_args__ = {"schema": "stackoverflow"}
organization_id: int | None = Field(default=None, primary_key=True)
organization_name: str
services: list["ServiceData"] = Relationship(link_model=OrganizationServiceData)
workspaces: list["WorkspaceData"] = Relationship(link_model=OrganizationWorkspaceData)
@nrec
def to_organization(self):
return Organization(
id=self.organization_id,
name=self.organization_name,
services=[service.to_service() for service in self.services],
workspaces=[w for workspace in self.workspaces if (w := workspace.to_workspace())] or None
)
class Organization(BaseModel):
id: int | None = None
name: str
services: list[Service]
workspaces: list["Workspace"] | None
class WorkspaceData(SQLModel, table=True):
__tablename__ = "workspace"
__table_args__ = {"schema": "stackoverflow"}
workspace_id: int | None = Field(default=None, primary_key=True)
workspace_name: str
organization_id: int = Field(foreign_key=".organization.organization_id", ondelete="CASCADE")
organization: OrganizationData = Relationship()
services: list["ServiceData"] = Relationship(link_model=WorkspaceServiceData)
@nrec
def to_workspace(self):
return Workspace(
id=self.workspace_id,
name=self.workspace_name,
organization=self.organization.to_organization(),
services=[service.to_service() for service in self.services]
)
class Workspace(BaseModel):
id: int | None
name: str
organization: Organization | None
services: list[Service]
From an organization I want to get the workspaces, and from a workspace I want to get the organization. But of course, I have no interest in having the workspace's organization of an already given organization. Hope it makes sense.
Please tell me if this is the correct solution or if there is a cleaner way to do this.
Perhaps.