Общая связь с общим fk: Не удается избавиться от предупреждения о перекрытии

#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 во время оценки объекта таблицы, новая связь сравнивается с параметром перекрытия. Мне потребовалось несколько проб и ошибок, чтобы все сделать правильно.