Search code examples
sqlalchemymany-to-manyfastapicircular-dependency

FastAPI many to many relationship, multiple models and schemas (circular dependencies error)


i'm new with FastAPI, actually i spend much time trying solve my problem, i read many and test diferente Solutions but not working. The Problem is this:

I have a table School, my model and my schema:

model/school.py

from sqlalchemy.orm import relationship

class School(BASE):
    __tablename__ = "school"
    ...

    careers = relationship("Career", back_populates="school")

schema/school.py

from src.schemas.v1.career import Career

class School(BaseModel):
    id: Optional[int] = None
    ...
    careers: list[Career] = []
    
    class Config:
        orm_mode = True

Then my relation many to many between Career and Employee, CareerHasEmployee

models/career.py

from sqlalchemy.orm import relationship

class Career(BASE):
    __tablename__ = "career"
    ...
    school_id = Column(Integer, ForeignKey("school.id"))

    # Relations
    school = relationship("School", back_populates="careers")
    ...
    employees = relationship("Employee", secondary="career_has_employee", backref="career_employee")

models/career_has_employee.py

from sqlalchemy.orm import relationship

class CareerHasEmployee(BASE):
    __tablename__ = "career_has_employee"
    career_id = Column(Integer, ForeignKey("career.id"), primary_key=True)
    employee_id = Column(Integer, ForeignKey("employee.id"), primary_key=True)

models/employee.py

from sqlalchemy.orm import relationship

class Employee(BASE):
    __tablename__ = "employee"
    id = Column(Integer, primary_key = True)
    ...

    careers = relationship("Career", secondary="career_has_employee", backref="employee_career")

When testing the relation from consolé in Python, get data correctly, but when testing since fastapi i get error for circular dependency, this error happens when load schemas.

schemas/career.py

from src.schemas.v1.employee import Employee

class CareerBase(BaseModel):
    id: Optional[int] = None
    ...
    school_id: int

    class Config:
        orm_mode = True

class CareerEmployee(CareerBase):
    employees: list[Employee] = []

schemas/employee.py

...
from src.schemas.v1.career import Career <- When use this line i get error for circular dependency, this is because in schema/school.py already import this Schema

class EmployeeBase(BaseModel):
    id: Optional[int] = None
    ...

    class Config:
        orm_mode = True

class EmployeeCareer(EmployeeBase):
    careers: list[Career] = []

Response host/careers/{id}/employees

{
  "id": 1,
  "nombre": "Ingenieria en Sistemas Computacionales",
  "employees": [
    {
      "id": 1,
      "nombre": "Jhon",
      "apellidos": "Wick"
    },
    {
      "id": 2,
      "nombre": "Kymberly",
      "apellidos": "Thanos"
    }
  ]
}

When try this endpoint host/employees/{id}/careers

I get error for circular dependency

Note: Already i try:

  • from future import annotations and update_forward_refs for diferent ways
  • use TYPE_CHECKING
  • add all schemas in init.py and update_forward_refs and not working u.u

Solution

  • The error is given from the circular dependency that is within the schema.

    That is, you are declaring that career requires to import from employee and employee to import from career. As soon as the program one of the two, it'll try to fetch the other one, which will require the first one, which requires the second one in order to resolve and so on ... it's an infinite loop.

    Solution

    The only solution you have is to create to base schema (one for employee and one for career) and then two other "auxiliary" schema that will be returned from your API which will contain both.

    Otherwise, ask yourself if it's really necessary to return all that data...do the careers really need all the employees? Do the employees really need the careers?

    Personally, I would drop the careers from the employees as this would imply to fetch a potentially huge amount of data that may not be needed.