Используя SQLAlchemy с движком SQLite, я получил иерархическую таблицу с самообращением, которая описывает структуру каталогов.
from sqlalchemy import Column, Integer, String, ForeignKey, Index
from sqlalchemy.orm import column_property, aliased, join
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Dr(Base):
__tablename__ = 'directories'
id = Column(Integer, primary_key=True)
name = Column(String)
parent_id = Column(Integer, ForeignKey('directories.id'))
Каждая строка Dr знает только свое «имя» и свой «родительский_идентификатор»,Я добавил рекурсивный column_property под названием «путь», который возвращает строку, содержащую всех предков доктора из корня Dr.
root_anchor = (
select([Dr.id, Dr.name, Dr.parent_id,Dr.name.label('path')])
.where(Dr.parent_id == None).cte(recursive=True)
)
dir_alias = aliased(Dr)
cte_alias = aliased(root_anchor)
path_table = root_anchor.union_all(
select([
dir_alias.id, dir_alias.name,
dir_alias.parent_id, cte_alias.c.path + "/" + dir_alias.name
]).select_from(join(
dir_alias, cte_alias, onclause=cte_alias.c.id==dir_alias.parent_id)
))
)
Dr.path = column_property(
select([path_table.c.path]).where(path_table.c.id==Dr.id)
)
Вот пример вывода:
"""
-----------------------------
| id | name | parent_id |
-----------------------------
| 1 | root | NULL |
-----------------------------
| 2 | kid | 1 |
-----------------------------
| 3 | grandkid | 2 |
-----------------------------
"""
sqllite_engine = create_engine('sqlite:///:memory:')
Session = sessionmaker(bind=sqllite_engine)
session = Session()
instance = session.query(Dr).filter(Dr.name=='grandkid').one()
print(instance.path)
# Outputs: "root/kid/grandkid"
Я хотел бы иметь возможность добавить индекс или, по крайней мере, уникальное ограничение для свойства «путь», чтобы уникальные пути не могли существовать более одного раза в таблице.Я пробовал:
Index('pathindex', Directory.path, unique=True)
... без удачи.Ошибка не возникает, но SQLAlchemy, похоже, не регистрирует индекс, а просто игнорирует его.Он по-прежнему позволяет добавлять повторяющийся путь, например:
session.add(Dr(name='grandkid', parent_id=2))
session.commit()
Поскольку дальнейшее свидетельство того, что Index () был проигнорирован, проверка свойства "indexes" таблицы приводит к пустому набору:
print(Dr.__table__.indexes)
#Outputs: set([])
Для меня важно, чтобы в базе данных не существовало повторяющихся путей.Я не уверен, возможно ли то, что я пытаюсь сделать с column_property, в SQLAlchemy, и если нет, то я бы хотел услышать некоторые предложения о том, как еще я могу это сделать.