WedX - журнал о программировании и компьютерных науках

Создание дерева из самореферентных таблиц в SQLalchemy

Я создаю базовую CMS в колбе для сайта, ориентированного на iPhone, и у меня возникли небольшие проблемы с чем-то. У меня очень маленькая база данных всего с 1 таблицей (страницами). Вот модель:

class Page(db.Model):
    __tablename__ = 'pages'
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    content = db.Column(db.Text, nullable=False)
    parent_id = db.Column(db.Integer, db.ForeignKey("pages.id"), nullable=True)

Как видите, для подстраниц они просто ссылаются на другой объект страницы в поле parent_id. То, что я пытаюсь сделать в панели администратора, - это вложенный неупорядоченный список со всеми страницами, вложенными в их родительские страницы. У меня очень мало идей о том, как это сделать. Все, о чем я могу думать, это следующее (которое будет работать (возможно, я не проверял) только на 2 уровня ниже):

pages = Page.query.filter_by(parent_id=None)
for page in pages:
    if Page.query.filter_by(parent_id=page.id):
        page.sub_pages = Page.query.filter_by(parent_id=page.id)

Затем я бы просто отформатировал его в список в шаблоне. Как мне заставить это работать с потенциально более чем 10 вложенными страницами?

Заранее спасибо!


EDIT: я немного осмотрелся и нашел https://www.sqlalchemy.org/docs/orm/relationships.html#adjacency-list-relationships, поэтому я добавил

children = db.relationship("Page", backref=db.backref("parent", remote_side=id))

в нижней части моей модели Page. и я смотрю на рекурсивное прохождение всего и добавление его в дерево объектов. Я, наверное, не понял смысла, но это лучший способ, которым я могу это описать.


РЕДАКТИРОВАТЬ 2: я попытался создать рекурсивную функцию для прохождения по всем страницам и создания большого вложенного словаря со всеми страницами и их дочерними элементами, но он продолжает сбой Python, поэтому я думаю, что это просто бесконечный цикл... вот функция

def get_tree(base_page, dest_dict):
    dest_dict = { 'title': base_page.title, 'content': base_page.content }
    children = base_page.children
    if children:
        dest_dict['children'] = {}
        for child in children:
            get_tree(base_page, dest_dict)
    else:
        return

и страница, на которой я это тестирую:

@app.route('/test/')
def test():
    pages = Page.query.filter_by(parent_id=None)
    pages_dict = {}
    for page in pages:
        get_tree(page, pages_dict)
    return str(pages_dict)

у кого-нибудь есть идеи?

04.02.2011

Ответы:


1

Посмотрите на https://sqlamp.angri.ru/index.html

или https://www.sqlalchemy.org/trac/browser/examples/adjacency_list/adjacency_list.py

UPD: Для декларативного примера adjacency_list.py

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base(metadata=metadata)

class TreeNode(Base):

    __tablename__ = 'tree'

    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey('tree.id'))
    name = Column(String(50), nullable=False)

    children = relationship('TreeNode',

                        # cascade deletions
                        cascade="all",

                        # many to one + adjacency list - remote_side
                        # is required to reference the 'remote' 
                        # column in the join condition.
                        backref=backref("parent", remote_side='TreeNode.id'),

                        # children will be represented as a dictionary
                        # on the "name" attribute.
                        collection_class=attribute_mapped_collection('name'),
                    ) 

    def __init__(self, name, parent=None):
        self.name = name
        self.parent = parent

    def append(self, nodename):
        self.children[nodename] = TreeNode(nodename, parent=self)

    def __repr__(self):
        return "TreeNode(name=%r, id=%r, parent_id=%r)" % (
                    self.name,
                    self.id,
                    self.parent_id
                )    

Исправить рекурсию

def get_tree(base_page, dest_dict):
    dest_dict = { 'title': base_page.title, 'content': base_page.content }
    children = base_page.children
    if children:
        dest_dict['children'] = {}
        for child in children:
            get_tree(child, dest_dict)
    else:
        return

Используйте запрос в примере для рекурсивной выборки данных из БД:

 # 4 level deep
 node = session.query(TreeNode).\
                        options(joinedload_all("children", "children", 
                                                "children", "children")).\
                        filter(TreeNode.name=="rootnode").\
                        first()
04.02.2011
  • Спасибо за это, но это все еще немного над моей головой. для второй ссылки есть ли способ сделать это с помощью декларативного базового способа определения моделей (это то, что использует расширение flask для sqlalchemy)? 05.02.2011
  • @Estin Этот вызов joinload_all указывает дочерние элементы N раз. В данном случае 4, поэтому дерево будет рекурсивно только 4 раза. Есть ли способ заставить его повторяться произвольное количество раз? Разве есть программный способ определить это легко? 12.07.2014
  • @Zoran, если вы планировали работать с рекурсиями деревьев, это не лучший выбор, более мощными способами являются использование решений MPTT, таких как sqlalchemy-mptt.readthedocs.org/en/latest › Если нет программного способа легко определить это? - Вы можете хранить на корневом узле его глубокий уровень. 18.07.2014
  • @ZoranPavlovic - прошло больше года с тех пор, как вы задали свой вопрос, однако я подумал, что дам вам еще одну подсказку. Вы можете просто получить все строки в одном запросе (то есть без options(joinedload_all(...)), а затем построить свое дерево на основе parent_id. Я предполагаю, что у вас не будет миллионов элементов (т.е. сколько страниц у вас будет? 100? даже 1000? ), поэтому такой запрос не должен создавать проблем с точки зрения производительности базы данных, а также обход такого массива для построения вашего дерева не должен быть слишком ресурсоемким. 24.08.2015
  • В чем разница между аргументом cascade в relationship и аргументом ONDELETE в функции foreignkey, для которой установлено значение CASCADE? 02.05.2019
  • После этого сообщения joinedload_all устарело и будет удален в будущем выпуске. 22.06.2020
  • Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    Для любых предложений по сайту: [email protected]