#sqlalchemy
Вопрос:
В базе данных, которую я не контролирую, у меня есть то, что я считаю общей ассоциацией, использующей общие внешние ключи. Короче говоря: интерфейс связи принадлежит либо маршрутизатору, либо Серверу, но никогда обоим. Здесь нет дискриминатора, потому id
что s уникальны во всей базе данных.
Код сокращен для краткости:
class Server(...):
__tablename__ = 'server'
id = Column('_oid', Integer, primary_key=True)
interfaces = relationship(
'CommunicationInterface', backref='server',
primaryjoin='Server.id == foreign(CommunicationInterface.parent_id)'
)
class Router(...):
__tablename__ = 'router'
id = Column('_oid', Integer, primary_key=True)
interfaces = relationship(
'CommunicationInterface', backref='router',
primaryjoin='Router.id == foreign(CommunicationInterface.parent_id)'
)
class CommunicationInterface(...):
__tablename__ = 'communicationinterface'
id = Column('_oid', Integer, primary_key=True)
parent_id = Column('_parent_oid', Integer)
overlaps = 'router,server'
router = relationship(
'Router', back_populates='interfaces', overlaps=overlaps,
primaryjoin='Router.id == foreign(CommunicationInterface.parent_id)'
)
server = relationship(
'Server', back_populates='interfaces', overlaps=overlaps,
primaryjoin='Server.id == foreign(CommunicationInterface.parent_id)'
)
Даже с параметром перекрытия это все равно дает мне предупреждения, такие как следующее:
Предупреждение: отношение «Маршрутизатор.интерфейсы» скопирует маршрутизатор столбца._оид к интерфейсу связи столбца._parent_oid, который конфликтует с отношениями: ‘CommunicationInterface.server’ (копирует elb_server._oid в elb_communicationinterface._парентоид). Если это не входит в намерения, подумайте, должны ли эти отношения быть связаны с back_populates или если viewonly=True должно применяться к одному или нескольким, если они доступны только для чтения. В менее распространенном случае, когда ограничения внешнего ключа частично перекрываются, аннотацию orm.foreign() можно использовать для выделения столбцов, в которые следует писать. Параметр «перекрытия» может быть использован для удаления этого предупреждения.
Есть ли лучший способ заявить об этих отношениях? А если нет, то я overlaps
неправильно использую параметр?
Ответ №1:
Проблема, с которой вы столкнулись, имеет несколько причин:
- Смешивание
backref
иback_populates
backref
также создаетrelationship
на другой сторонеback_populates
ожидает аrelationship
с другой стороны
overlaps
неверно отсутствуетrelationship
на другой стороне
Это должно решить вашу проблему:
class Server(Base):
__tablename__ = 'server'
id = Column('_oid', Integer, primary_key=True)
class Router(Base):
__tablename__ = 'router'
id = Column('_oid', Integer, primary_key=True)
class CommunicationInterface(Base):
__tablename__ = 'communicationinterface'
id = Column('_oid', Integer, primary_key=True)
parent_id = Column('_parent_oid', Integer)
router = relationship(
'Router', backref=backref('interface', lazy='subquery', overlaps='server, interface'),
primaryjoin='Router.id == foreign(CommunicationInterface.parent_id)'
)
server = relationship(
'Server', backref=backref('interface', lazy='subquery', overlaps='router'), overlaps='router',
primaryjoin='Server.id == foreign(CommunicationInterface.parent_id)'
)
Или, если вы хотите сделать это с back_populates
:
class Server(Base):
__tablename__ = 'server'
id = Column('_oid', Integer, primary_key=True)
interface = relationship(
'CommunicationInterface', back_populates='server', overlaps='router',
primaryjoin='Server.id == foreign(CommunicationInterface.parent_id)'
)
class Router(Base):
__tablename__ = 'router'
id = Column('_oid', Integer, primary_key=True)
interface = relationship(
'CommunicationInterface', back_populates='router', overlaps='server, interface',
primaryjoin='Router.id == foreign(CommunicationInterface.parent_id)'
)
class CommunicationInterface(Base):
__tablename__ = 'communicationinterface'
id = Column('_oid', Integer, primary_key=True)
parent_id = Column('_parent_oid', Integer)
router = relationship(
'Router', back_populates='interface',
primaryjoin='Router.id == foreign(CommunicationInterface.parent_id)'
)
server = relationship(
'Server', back_populates='interface', overlaps='router',
primaryjoin='Server.id == foreign(CommunicationInterface.parent_id)'
)
Комментарии:
1. Спасибо. К сожалению, это все еще не работает для меня, хотя. Ошибка
backref
/backpopulates
произошла во время устранения неполадок. Возможно, уместно: на самом деле у меня есть еще одна сущность, указывающая наCommunicationInterface
помимоServer
иRouter
. Кроме того, какова логика дляoverlaps
параметра? Вы, кажется, используете'server, interface'
в одном, но толькоrouter
в другом.2. Я принял этот ответ, потому что благодаря этому я наконец-то заставил его работать. Единственное, чего, по-видимому, не хватает, — это
overlaps='server, interface'
параметра вrouter
отношении (не в обратной ссылке) в вашем первом примере.3. Определение перекрытия, на мой взгляд, немного расплывчато. Но, похоже, это связано с порядком определения. Я посмотрел в базе кода a во время оценки объекта таблицы, новая связь сравнивается с параметром перекрытия. Мне потребовалось несколько проб и ошибок, чтобы все сделать правильно.