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

Для репрезентации соответствующие метаданные в таблицах SQL «Файл» и «Папка» будут такими: ['object_id', 'parent_id', 'child_nodes'] где,

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

Я создал классы 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()
1
mfnight 10 Дек 2020 в 03:53

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
Kapocsi 16 Дек 2020 в 10:36