Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQLAlchemy Model Circular Import

I have two models in the same module named models. They are a 1-1 relationship and have been configured per the SQLAlchemy docs.

Vehicle.py

from models.AssetSetting import AssetSetting

class Vehicle(Base):
     __tablename__ = 'vehicles'

     vehicle_id = Column(Integer, primary_key=True)
     ...
     settings = relationship('AssetSetting', backref=backref('asset_settings'))

AssetSetting.py

from models.Vehicle import Vehicle

class AssetSetting(Base):
     __tablename__ = 'asset_settings'

     asset_alert_setting_id = Column(Integer, primary_key=True, autoincrement=True)
     ...

     vehicle = relationship('vehicles', foreign_keys=Column(ForeignKey('vehicles.vehicle_id')))

If I use the string relationship building (i.e. ForeignKey('vehicles.vehicle_id')) I get the error:

sqlalchemy.exc.InvalidRequestError: 
When initializing mapper Mapper|AssetSetting|asset_settings, expression 'vehicles' failed to locate a name ("name 'vehicles' is not defined"). 
If this is a class name, consider adding this relationship() to the <class 'models.AssetSetting.AssetSetting'> class after both dependent classes have been defined.

If I use the class mapping, I get the classic circular import error:

Traceback (most recent call last):
File "tracking_data_runner.py", line 7, in <module>
from models.Tracker import Tracker
File "/.../models/Tracker.py", line 5, in <module>
from models.Vehicle import Vehicle
File "/.../models/Vehicle.py", line 13, in <module>
from models.Tracker import Tracker
ImportError: cannot import name 'Tracker'

I believe I could fix this issue by putting the files in the same package but would prefer to keep them separate. Thoughts?

like image 668
mmcclannahan Avatar asked Dec 22 '15 17:12

mmcclannahan


1 Answers

To avoid circular import errors, you should use string relationship building, but both of your models have to use the same Base - the same declarative_base instance. Instantiate your Base once and use it when initializing both Vehicle and AssetSetting.

Or, you may explicitly map the table names and classes to help mapper relate your models:

Base = declarative_base(class_registry={"vehicles": Vehicle, "asset_settings": AssetSetting})
like image 94
alecxe Avatar answered Oct 21 '22 02:10

alecxe