Циркулярный импорт модели SQLAlchemy

У меня две модели в том же модуле с именем models. Они являются отношениями 1-1 и настроены для 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')))

Если я использую строковое построение отношений (т.е. ForeignKey('vehicles.vehicle_id')), я получаю ошибку:

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.

Если я использую сопоставление классов, я получаю классическую циклическую ошибку импорта:

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'

Я считаю, что могу исправить эту проблему, поместив файлы в один и тот же пакет, но предпочел бы, чтобы они были разделены. Мысли?

Ответ 1

Я обнаружил, что моя проблема была в два раза:

  • Я неправильно ссылался на Vehicles в моих отношениях. Это должно быть relationship('Vehicle' not relationship('vehicles'
  • По-видимому, неверно объявлять FK внутри отношения, как это было в AssetSettings.py (foreign_keys=Column(ForeignKey('vehicles.vehicle_id'))). Я должен был объявить FK, а затем передать его в отношения.

Теперь мои конфигурации выглядят следующим образом:

Vehicle.py

class Vehicle(Base, IDiagnostable, IUsage, ITrackable):
    __tablename__ = 'vehicles'

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

AssetSetting.py

class AssetSetting(Base):
    __tablename__ = 'asset_settings'

    asset_alert_setting_id = Column(Integer, primary_key=True, autoincrement=True)
    vehicle_id = Column(ForeignKey('vehicles.vehicle_id'))

    vehicle = relationship('Vehicle', foreign_keys=vehicle_id)

Ответ 2

Чтобы избежать циклических ошибок импорта, вы должны использовать построение строковых связей, но обе ваши модели должны использовать тот же Base - тот же экземпляр declarative_base. Создайте экземпляр Base один раз и используйте его при инициализации как Vehicle, так и AssetSetting.

Или вы можете явно сопоставить имена и классы таблиц, чтобы помочь сопоставлять ваши модели:

Base = declarative_base(class_registry={"vehicles": Vehicle, "asset_settings": AssetSetting})

Ответ 3

Ваш __tablename__ ссылается на vehicles, но ваш внешний ключ ссылается на vehicle.vehicle_id