Search code examples
python-3.xfastapisqlmodel

How can I resolve circular dependencies with FastAPI and SQLModel?


I have a tree like

.
├── app
│   ├── __init__.py
│   ├── config.py
│   ├── database.py
│   ├── dependencies.py
│   ├── main.py
│   ├── models
│   │   ├── __init__.py
[..]
│   │   ├── routines.py
│   │   └── routine_steps.py
│   ├── routers
│   │   ├── __init__.py
[...]
│   │   ├── routines.py
├── requirements.txt
└── wsgi.py

an app/models/__init__.py like:

# ...
from .routine_section import RoutineSection
from .routine_step import RoutineStep
from .routine import Routine

and model files like app/models/routine.py:

from typing import Optional, List, Dict, Any
from pydantic import UUID4
from datetime import datetime
from sqlmodel import Field, Relationship, SQLModel, Session, select
from collections import OrderedDict
import uuid

from .routine_section import RoutineSection
from .routine_step import RoutineStep

class Routine(SQLModel, table=True):
  id: Optional[UUID4] = Field(default_factory=uuid.uuid4, primary_key=True)
  name: Optional[str] = None
  description: Optional[str] = None
  operation_id: Optional[UUID4] = Field(None, foreign_key="operations.id")
  sections: List["RoutineSection"] = Relationship(back_populates="routine", sa_relationship_kwargs={"order_by": "RoutineSection.routine_id"})
  steps: List["RoutineStep"] = Relationship(back_populates="routine", sa_relationship_kwargs={"order_by": "RoutineStep.position"})

And I'm getting errors like:

  File "/path/app/main.py", line 9, in <module>
    from .routers import routines
  File "/path/app/routers/db.py", line 15, in <module>
    from app.models import Routine
  File "/path/app/models/__init__.py", line 4, in <module>
    from .operation import Operation
  File "/path/app/models/operation.py", line 7, in <module>
    from .routine import Routine
  File "/path/app/models/routine.py", line 8, in <module>
    from .routine_section import RoutineSection
  File "/path/app/models/routine_section.py", line 7, in <module>
    from .routine_step import RoutineStep
  File "/path/app/models/routine_step.py", line 7, in <module>
    from .routine import Routine
ImportError: cannot import name 'Routine' from partially initialized module 'app.models.routine' (most likely due to a circular import) (/path/app/models/routine.py)

Solution

  • It is a cyclic import. routine is importing from routine_section, routine_section is importing from routine_step, and routine_step is importing from routine which has not been completely initialized.

    Since you are importing for type-hinting purposes, you can update the code with the TYPE_CHECKING flag.

    Here's a sample:

    from typing import TYPE_CHECKING
    
    if TYPE_CHECKING:
        from .routine_section import RoutineSection
        from .routine_step import RoutineStep
    
    
    class Routine(SQLModel, table=True):
        ...
        steps: List["RoutineStep"] = Relationship(back_populates="routine", sa_relationship_kwargs={"order_by": "RoutineStep.position"})