2012-05-14 2 views
4

Что делать, если у меня было что-то вроде двойного связанного списка в реляционной базе данных, например:Использование SQLAlchemy с несколькими автореферентных внешних ключей

node_id left_id right_id 

1   null  2 
2   1   3 
3   2   null 

Тогда у меня есть некоторый SQLAlchemy код вроде следующего:

class NodeClass(Base): 
    __tablename__ = 'nodes_table' 
    node_id = Column(Integer, primary_key=True) 
    left_id = Column(Integer, ForeignKey('nodes_table.node_id')) 
    right_id = Column(Integer, ForeignKey('nodes_table.node_id')) 
    left = relationship('NodeClass') # Wrong 
    right = relationship('NodeClass') # Wrong 

Если у меня есть node_id 2, и я звоню NodeClass.left Я хотел бы получить node_id 1 взамен. Как я могу настроить отношения SQLAlchemy таким образом?

ОБНОВЛЕНИЕ:

дам второй пример. Рассмотрим таблицу людей, и у каждого человека есть мать и отец.

person_id mother_id father_id 

1   null   null 
2   null   null 
3   1   2 

Код SQLAlchemy:

class PersonClass(Base): 
    __tablename__ = 'persons_table' 
    person_id = Column(Integer, primary_key=True) 
    mother_id = Column(Integer, ForeignKey('persons_table.person_id')) 
    father_id = Column(Integer, ForeignKey('persons_table.person_id')) 
    mother = relation('PersonClass') # Wrong 
    father = relation('PersonClass') # Wrong 

ответ

4

Приведенный ниже код показывает, как настроить связь:

class NodeClass(Base): 
    __tablename__ = 'nodes_table' 
    node_id = Column(Integer, primary_key=True) 

    left_id = Column(Integer, ForeignKey('nodes_table.node_id')) 
    left = relationship('NodeClass', remote_side=[node_id], 
      primaryjoin=('NodeClass.left_id==NodeClass.node_id'), 
      backref=backref("right", uselist=False), 
      #lazy="joined", join_depth=9, 
      ) 

Но несколько вещей, которые следует отметить:

  • только один конец отношения сохраняется, другой - inferr редактор Возможно, это не так, как вам хочется, но это намного проще в управлении, и достаточно установить одну сторону только myNode.left = myOtherNode, а другую (right) будет установлена ​​автоматически (из-за сконфигурированного backref)
  • , если оба конца хранится (right и left), затем
    • оба конца должны быть установлены в коде и один должен гарантировать, что они последовательны, которые не могли бы быть тривиальной задачей
    • вставка из двух узлов, которые связаны требует insert-1, insert-2, update-1 в случае, если ваш первичный ключ вычисляется в базе данных, так как он неизвестен в течение первого insert.

UPDATE: Пример кода в UPDATE части вопроса (но все еще используя имя исходного класса). Нужно только указать primaryjoin и uselist=False:

class NodeClass(Base): 
    __tablename__ = 'nodes_table' 
    node_id = Column(Integer, primary_key=True) 

    left_id = Column(Integer, ForeignKey('nodes_table.node_id')) 
    right_id = Column(Integer, ForeignKey('nodes_table.node_id')) 

    left = relationship('NodeClass', primaryjoin = ('NodeClass.left_id == NodeClass.node_id'), use_list=False) 
    right = relationship('NodeClass', primaryjoin = ('NodeClass.right_id == NodeClass.node_id'), use_list=False) 
+0

двойной связанный список примеров был самый простой пример, который я мог придумать. На самом деле у меня есть более сложные данные, и я не могу изменить схему базы данных. Основная проблема, с которой я столкнулся, заключалась в множественных внешних ссылочных ключах, но вы удалили их из проблемы, поэтому ваше решение напрямую не применимо. Тем не менее, это дает мне хорошую отправную точку. – Buttons840

+0

@ Buttons840: ваш второй пример довольно прост в ответе: для каждого отношения (отец/месяц или слева/справа) вам нужно указать условие соединения с помощью * primaryjoin *. В ближайшее время я добавлю ответ. – van

+0

У меня проблемы с primaryjoin. Поскольку оба операнда на equals (==) являются NodeClass, как он знает, какой NodeClass является текущим узлом, а какой NodeClass является связанным узлом? Когда я прошу «левый», его можно интерпретировать как «дать мне другой узел, id которого равен моему left_id» или «дать мне другой узел, у которого left_id равен моему id». Все наоборот. Я пробовал это, и он не работает.Я думаю, мне нужно использовать таблицу с псевдонимом, но я не знаю, как это сделать в декларативном классе. – Buttons840

Смежные вопросы