Проблема с подкаталогом при создании файловой системы в памяти для веб-приложения Flask

#python #python-3.x #flask #data-structures #filesystems

#python #python-3.x #flask #структуры данных #файловые системы

Вопрос:

Я создаю виртуальную файловую систему / файловую систему в памяти как часть приложений Flask, где пользователь создает файлы и папки, и я сохраняю их в базе данных SQL и отображаю дерево каталогов обратно пользователю в пользовательском интерфейсе (например, dropbox / google Drive).

Ради reprex соответствующие метаданные в таблицах SQL «File» и «Folder» будут следующими: ['object_id', 'parent_id', 'child_nodes'] где,

  • object_id = уникальный идентификатор
  • parent_id = идентификатор родительского объекта_id, к которому принадлежит вложенный файл или вложенная папка
  • child_nodes = дочерний объект_id родительского объекта

Я создал классы Project и File для обработки внутренних методов и свойств (исключены, но необходимы). Поэтому в идеале мне нужны классы в моем конечном решении.

Основная проблема, с которой я сталкиваюсь, — это добавление подкаталогов child_nodes к каталогам.Это наблюдается [в закомментированном коде внизу] при итерации from dir_list[1] to dir_list[2] , где dir_list[1] уже было бы добавлено dir_list[0] и, следовательно, не будет отражать следующую итерацию.

Ищите какие-либо предложения о том, как это реализовать. Я также полностью открыт для использования разных структур данных, если я могу добавлять метаданные и форматировать их так же, как это FileDir.create_tree() делается.Примечание: мне нужно перебирать теоретически бесконечное количество подкаталогов, а не только то, что находится в моем reprex.

 # Objects for organizing each struct -----
class File():
    def __init__(self, file_list):
        self.id = file_list[0]
        self.name = file_list[1]
        self.parent = file_list[2]
        self.directory = False

class Directory:
    def __init__(self, dir_list):
        self.id = dir_list [0]
        self.name = dir_list [1]
        self.parent = dir_list [2]
        self.child_nodes = []
        self.directory = True

    def add_file_node(self, node):

        node = {
                'id': node.id,
                'name':  node.name,
                'parent': self.parent,
                'is_dir': node.directory
            }
        self.child_nodes.append(node)

    def add_dir_node(self, node):

        node = {
                'id': node.id,
                'name':  node.name,
                'parent': self.parent,
                'is_dir': node.directory,
                'children': self.child_nodes
            }
        self.child_nodes.append(node)


    def return_tree(self):
        tree = {
            'name': self.name,
            'children': self.child_nodes,
            'parent': self.parent,
            'is_directory': self.directory
        }
        return tree



class FileDir():
    def __init__(self, dir_list):
        self.dir_list = dir_list
    def create_tree(self):
        tree = []
        for directory in self.dir_list:
            tree.append(directory.return_tree())
        return tree

# Example Data (formatted as 2d-list from my SQL query) -----
dir_list = [
         ['10001', 'dir_1', None],
         ['10002', 'dir_2', '10001'],
         ['10003', 'dir_3', '10002'],
         ['10004', 'dir_4', None]
     ]

file_list = [
         ['21110', 'file1.csv', None],
         ['21111', 'file2.csv', '10001'],
         ['21112', 'file3.csv', '10002'],
         ['21113', 'file3.csv', '10003']
     ]

dir_objs = [Directory(d) for d in dir_list]
file_objs = [File(f) for f in file_list]

for fil in file_objs:
    if fil.parent:
        for i, x in enumerate(dir_objs):
            if fil.parent == x.id:
                x.add_file_node(fil)


# TODO Append sub_folders
# ...
# 
# for d in dir_objs:
#    if d.parent:
#        for i, x in enumerate(dir_objs):
#            if d.parent == x.id:
#                x.add_dir_node(d)
#                dir_objs.remove(d)

tree = FileDir(dir_objs)
tree.create_tree()
 

Ответ №1:

Соответствует ли этот код вашим потребностям?

 
# Objects for organizing each struct -----
class File:
    def __init__(self, file_list):
        self.id = file_list[0]
        self.name = file_list[1]
        self.parent = file_list[2]
        self.directory = False

class Directory:
    def __init__(self, dir_list):
        self.id = dir_list[0]
        self.name = dir_list[1]
        self.parent = dir_list[2]
        self.child_nodes = []
        self.directory = True

    def add_file_node(self, node):

        node = {
                'id': node.id,
                'name':  node.name,
                'parent': self.parent,
                'is_dir': node.directory
            }
        self.child_nodes.append(node)

    def add_dir_node(self, node):

        node = {
                'id': node.id,
                'name':  node.name,
                'parent': self.parent,
                'is_dir': node.directory,
                'children': self.child_nodes
            }
        self.child_nodes.append(node)


    def return_tree(self):
        tree = {
            'name': self.name,
            'children': self.child_nodes,
            'parent': self.parent,
            'is_directory': self.directory
        }
        return tree



class FileDir:
    def __init__(self, dir_list):
        self.dir_list = dir_list
    def create_tree(self):
        tree = []
        for directory in self.dir_list:
            tree.append(directory.return_tree())
        return tree

# Example Data (formatted as 2d-list from my SQL query) -----
dir_list = [
         ['10001', 'dir_1', None],
         ['10002', 'dir_2', '10001'],
         ['10003', 'dir_3', '10002'],
         ['10004', 'dir_4', None]
     ]

file_list = [
         ['21110', 'file1.csv', None],
         ['21111', 'file2.csv', '10001'],
         ['21112', 'file3.csv', '10002'],
         ['21113', 'file3.csv', '10003']
     ]

dir_objs = [Directory(d) for d in dir_list]
file_objs = [File(f) for f in file_list]

for fil in file_objs:
    if fil.parent:
        for i, x in enumerate(dir_objs):
            if fil.parent == x.id:
                x.add_file_node(fil)


for dir_obj in dir_objs:
   if dir_obj.parent:
       for potential_parent_dir_obj in dir_objs:
           if dir_obj.parent == potential_parent_dir_obj.id:
               potential_parent_dir_obj.add_dir_node(dir_obj)
dir_objs = [dir_obj for dir_obj in dir_objs if not dir_obj.parent]



tree = FileDir(dir_objs)
tree.create_tree()

 

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


Редактировать

Вот более надежная версия, которую я разработал с помощью Python 3.8, которая должна обрабатывать произвольную глубину. Я не тестировал его подробно, но, надеюсь, это поможет. Нет запутанной рекурсии (на поверхности).

 from __future__ import annotations
from typing import Union, List
from dataclasses import dataclass, asdict, field
import json


@dataclass
class Node:
    node_id: str
    name: str
    parent_node_id: str = None

    def to_tree(self):
        return asdict(self)


@dataclass
class File(Node):
    is_directory: bool = False


@dataclass
class Directory(Node):
    is_directory: bool = True
    children: List[Union[Directory, File]] = field(default_factory=list)

    def add_child(self, child: Union[Directory, File]):
        self.children.append(child)


class FileSystem:

    def __init__(self, *nodes):
        self.nodes = {node.node_id: node for node in nodes}
        for node in self.non_root_nodes:
            self.nodes[node.parent_node_id].add_child(node)

    def __getitem__(self, node_id):
        return self.nodes[node_id]

    @property
    def root_nodes(self):
        return [node for node in self.nodes.values() if node.parent_node_id is None]

    @property
    def non_root_nodes(self):
        return [node for node in self.nodes.values() if node.parent_node_id is not None]

    @property
    def directories(self):
        return [node for node in self.nodes.values() if node.is_directory] 

    @property
    def files(self):
        return [node for node in self.nodes.values() if not node.is_directory]

    def to_tree(self):
        return [node.to_tree() for node in self.root_nodes]



dir_list = [
    # id,      name,    parent_node_id
     ['10001', 'dir_1', None], 
     ['10002', 'dir_2', '10001'],
     ['10003', 'dir_3', '10002'],
     ['10004', 'dir_4', None]
 ]

file_list = [
     ['21110', 'file1.csv', None],
     ['21111', 'file2.csv', '10001'],
     ['21112', 'file3.csv', '10002'],
     ['21113', 'file3.csv', '10003']
]

dir_list = [Directory(*directory) for directory in dir_list]
file_list = [File(*file) for file in file_list]

file_system = FileSystem(*dir_list, *file_list)
tree = file_system.to_tree() 

print(json.dumps(tree, indent=2))

 

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

1. Да, мне нужно, чтобы он прошел теоретически бесконечное количество уровней. Спасибо, что указали на это, я обновлю вопрос. Моя интуиция подсказывает мне, что это можно сделать с помощью надежного рекурсивного алгоритма, просто не уверен, каким будет базовый вариант. Но также не уверен, будет ли это эффективно с точки зрения вычислений

2. Нет проблем. — Сейчас 3:15 утра. Завтра я обновлю свой ответ более надежным решением, которое может обрабатывать произвольно глубокие иерархии.

3. @mfnight Ознакомьтесь с исправленным кодом, который я опубликовал выше.

4. Я также использую версию v3.8, и у меня не было проблем. Тесты также хорошо работают с большими наборами данных. Спасибо!