SQLAlchemy: как использовать ForeignKeyConstraint для родительской таблицы в полиморфном наследовании?

#python #sqlalchemy

#python #sqlalchemy

Вопрос:

У меня есть модель, где Edge объект ассоциации соединяет Node s и подразделяется на подклассы для создания разных типов Edge s. Моя конечная цель — динамически создавать все поля подклассов в самом Edge классе (или позже с помощью mixin).
Edge имеет составной первичный ключ, состоящий из двух внешних ключей к Node таблице и edge_type поля, которое также действует как дискриминатор для подклассов.
Для этого мне нужно подключить подклассы с помощью a ForeignKeyConstraint к составному первичному ключу Edge класса. Теперь, чтобы создать это ForeignKeyConstraint динамически, я определяю его с @declared_attr помощью , но не могу ссылаться на edge_type столбец, который должен быть унаследован из Edge таблицы в подклассы, но я получаю следующие исключения:

 File "/data/foo/venv/lib/python3.5/site-packages/sqlalchemy/util/_collections.py", line 194, in __getitem__
return self._data[key]
KeyError: 'edge_type'

During handling of the above exception, another exception occurred:

<snip>

sqlalchemy.exc.ArgumentError: Can't create ForeignKeyConstraint on table 'hierarchy': no column named 'edge_type' is present.
  

Моя модель выглядит так:

 class Edge(HasTablename, Base):

    from_node_id = Column(Integer, ForeignKey(Node.id), primary_key=True)
    to_node_id = Column(Integer, ForeignKey(Node.id), primary_key=True)
    from_node = relationship(Node, foreign_keys=from_node_id,
                             backref=backref('from_edges')
    to_node = relationship(Node, foreign_keys=to_node_id,
                           backref=backref('to_edges')

    edge_type = Column(String, primary_key=True)

    @declared_attr
    def __mapper_args__(cls):
        if has_inherited_table(cls):
            return {'polymorphic_identity': cls.__name__.lower()}
        else:
            return {'polymorphic_identity': cls.__name__.lower(),
                    'polymorphic_on': cls.edge_type}

    @declared_attr
    def __table_args__(cls):
        if has_inherited_table(cls):
            return (ForeignKeyConstraint(
                       ['from_node_id', 'to_node_id', 'edge_type'],
                       [cls.__base__().__table__.c.from_node_id,
                        cls.__base__().__table__.c.to_node_id,
                        cls.__base__().__table__.c.edge_type]), )


class Hierarchy(Edge):

    from_node_id = Column(Integer, primary_key=True)
    to_node_id = Column(Integer, primary_key=True)


class History(Edge):

    from_node_id = Column(Integer, primary_key=True)
    to_node_id = Column(Integer, primary_key=True)
  

Это отлично работает, если я явно определяю edge_type = Column(String, primary_key=True) в подклассах, но я не знаю, почему он не наследуется или не может быть доступен из ForeignKeyConstraint в @declared_attr ?

Комментарии:

1. «составной первичный ключ, состоящий из двух внешних ключей к таблице узлов и поля edge_type» , звучит как ужасная идея. Почему у вас нет обычного первичного ключа и уникального ограничения для них? Тогда класс, унаследованный от Edge , будет иметь простую ссылку на этот первичный ключ.

2. Спасибо, да, вы совершенно правы, я действительно должен просто использовать UniqueConstraint!