Я создаю виртуальную/в памяти файловую систему как часть приложений Flask, где пользователь создает файлы и папки, и я сохраняю их в базе данных SQL и отображаю дерево каталогов обратно пользователю в пользовательском интерфейсе ( подумайте о дропбоксе/гугл диске).
Для репрекса соответствующие метаданные в SQL-таблицах «Файл» и «Папка» будут следующими: ['object_id', 'parent_id', 'child_nodes']
где,
Я создал классы Project и File для обработки внутренних методов и свойств (исключенных, но необходимых). Поэтому в идеале мне нужны классы в моем конечном решении.
Основная проблема, с которой я сталкиваюсь, это добавление подкаталогов как child_nodes
к каталогам. Это видно [в закомментированном коде внизу] при итерации от dir_list[1]
к dir_list[2]
, где dir_list[1]
уже было бы добавлено к dir_list[0]
и, следовательно, не отражало бы следующую итерацию.
Ищу любое предложение о том, как это реализовать. Я также полностью открыт для использования различных структур данных, если я могу добавлять метаданные и форматировать их так же, как это делает FileDir.create_tree()
. Примечание. Мне нужно перебрать теоретически бесконечное количество подкаталогов, а не только то, что находится в моем репрексе.
# 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()
Соответствует ли этот код вашим потребностям?
# 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))
Без проблем. - Здесь 3:15 утра. Завтра я обновлю свой ответ более надежным решением, которое может обрабатывать произвольно глубокие иерархии.
@mfnight Ознакомьтесь с исправленным кодом, который я разместил выше.
Я также использую v3.8, и у меня не было проблем. Сравнительные тесты также достаточно хороши для больших наборов данных. Спасибо!
Да, мне нужно пройти теоретически бесконечное количество уровней. Спасибо, что указали на это, я обновлю вопрос. Моя интуиция подсказывает мне, что это можно сделать с помощью надежного рекурсивного алгоритма, просто я не уверен, каким будет базовый случай. Но также не уверен, будет ли это вычислительно эффективным