Search code examples
pythonfastapicircular-dependency

fastAPI multiple model file relationship causes circular dependency issue


I am relatively new to fastAPI.

I am developing a set of APIs. To make my project better structured, I separated my model into multiple files. For now, I have an Employee and Skills table. There is a one to many relationship from employee to skills, where each employee can have multiple skills which are tied together with employee_id.

This is my main class, that I have used routers:

    #main.py
    app.include_router(skill.router, prefix="/skill", tags=["skill"])
    app.include_router(employee.router, prefix="/employee", tags=["employee"])


    #employee.py
    Base = declarative_base()
    class Employee(Base):
        __tablename__ = "core_employees"
        employee_id = mapped_column(Integer, primary_key=True, index=True)
        first_name = mapped_column(String)
        last_name = mapped_column(String)
        skills = relationship(Skill)

    #skill.py
    Base = declarative_base()
    class Skill(Base):
        __tablename__ = "skills"
        skill_id = mapped_column(Integer, primary_key=True, index=True)
        employee_id = mapped_column(Integer, ForeignKey(Employee.employee_id))
        skill_name = mapped_column(String)
        skill_level = mapped_column(Integer)
        employee = relationship(Employee)

The file structure is like this:

    main.py
    model/
    │
    ├── employee.py
    ├── skill.py

I initially had difficulty with separating models into different files, as for instance employee = relationship('Employee') was not working, and I had to pass the actual class.

Now there is a problem. skill.py has to import employee.py and vice versa, which is a circular dependency problem.

I tried to add both imports to a package (make models a package) but it did not work for me. I also tried forward refs which also did not work. Please help me.

Thanks.


Solution

  • You need to use one common Base for all related models.

    
        #main.py
        app.include_router(skill.router, prefix="/skill", tags=["skill"])
        app.include_router(employee.router, prefix="/employee", tags=["employee"])
    
    
        #database.py
        Base = declarative_base()
    
    
        #employee.py
        from database import Base
        class Employee(Base):
            __tablename__ = "core_employees"
            employee_id = mapped_column(Integer, primary_key=True, index=True)
            first_name = mapped_column(String)
            last_name = mapped_column(String)
            skills = relationship("Skill" )
    
        #skill.py
        from database import Base
        from employee import Employee
        class Skill(Base):
            __tablename__ = "skills"
            skill_id = mapped_column(Integer, primary_key=True, index=True)
            employee_id = mapped_column(Integer, ForeignKey(Employee.employee_id))
            skill_name = mapped_column(String)
            skill_level = mapped_column(Integer)
            employee = relationship(Employee)