【问题标题】:Create a project management app in Django - DAGs and Dependencies在 Django 中创建项目管理应用程序 - DAG 和依赖项
【发布时间】:2020-12-13 16:28:18
【问题描述】:

我正在使用项目管理应用程序。应用程序需要的一点是创建具有依赖关系的任务的可能性,即任务 2 依赖于任务 1,任务 3 依赖于任务 2 和任务 B,等等。这将生成一个 DAG(有向无环图)。

问题是如何使用 Django 默认的 ORM 来存储这个“图”。

我提供了一个使用多对多关系(Task with self)和触发器来避免创建循环的解决方案,但我仍然不知道如何实现一些操作,例如从单个节点。

有没有人知道如何很好地实现它?例如,这将允许执行关键路径方法的实现。

编辑:

使用@Daniel 提出的解决方案,我最终得到了以下功能。我们可以看到,为了获得整个图,对数据库进行了多次调用。此函数在循环期间也会进入无限循环。

def get_task_graph(task, visited_nodes = [], graph = {}):
    # Visited nodes are nodes that all parents were visited
    # print(task)

    
    
    previous_tasks = task.previous_tasks.all()

    
    still_have_unmarked_parents = set(previous_tasks).difference(set(visited_nodes))
    if still_have_unmarked_parents:
        print(f"Task {task} still have unmarked parents")
        
        for previous_task in still_have_unmarked_parents:
            

            visited_nodes, graph =  get_task_graph(previous_task, visited_nodes=visited_nodes, graph=graph)
    
    next_tasks = task.next_tasks.all()
 
    visited_nodes.append(task)
    print(f"Marking {task} as visited")
    graph[task] = []
    for next_task in next_tasks:
        if next_task not in graph[task]:
            graph[task].append(next_task)
    
        
        visited_nodes, graph = get_task_graph(next_task, visited_nodes=visited_nodes, graph=graph)
        print(f"\t Adding {next_task} to {task}")

    return visited_nodes, graph

谢谢

【问题讨论】:

    标签: python sql django database orm


    【解决方案1】:

    原答案:多对多关系

    让我们创建一个简单的Task 模型:

    class Task(models.Model):
       name = models.CharField(...)
       previous_tasks = models.ManyToMany('Task', ..., related_name='next_tasks')
    

    现在让我们创建taks_atask_b - task_b 将依赖于task_a

    # create task_a, task_b:
    task_a = Task.objects.create(name='First Task')
    task_b = Task.objects.create(name='Second task')
    
    # add task_a as a dependency for task_b:
    task_b.previous_tasks.add([task_a])
    task_b.save()
    

    现在我们可以像这样访问这些任务:

    # get the tasks to do after task_a is complete:
    task_a = Task.objects.get(id='<task-a-id>')
    tasks_to_do_after_task_a = task_a.next_tasks.all() # returns a queryset with <task_b>
    
    # get the tasks we need to complete before task_b:
    task_b = Task.objects.get(id='<task-b-id>')
    tasks_that_task_b_depends_on = task_b.previous_tasks.all() # returns a queryset with <task a>
    

    最后,让我们假设 task_a 实际上有几个不同的任务依赖于它:

    # iterate over all the tasks that depend on task a:
    for tasks in task_a.next_tasks.all():
    
         # do some logic, i.e. find longest task:
         ...
    
    # filter for a specific next_task on task_a:
    some_task = task_a.next_tasks.filter(some_keyword='some_value')
    
    # assume we have a duration time-field on tasks:      
    next_longest_task = task_a.next_tasks.order_by('-duration')[0] # gets the next task with the longest duration
    

    您可以从那里根据需要实现寻路逻辑/其他算法。

    编辑:递归访问字段

    好的 - 使用上面的结构,我们可以像这样定义一个函数:

    def get_task_list(task):
    
        # get the next_tasks queryset, it will either contain tasks or be an empty queryset:
        next_tasks = task.next_tasks.all()
    
        # check if the current task has any children:
        if next_tasks:
    
            # do some logic, check for longest tasks / add next tasks to a list
            ...
    
            # pass each next task back into the function:
            for next_task in next_tasks:
                return get_task_list(next_task)
    
        # exit if there are no more next tasks:
        else:
    
            # do some logic on exiting, calculate the time to complete all tasks:
            ...
    
            return <optional-content>
    

    您可以对递归函数进行更多研究,但这应该可以帮助您入门。您可以在函数中向上查看以前的任务或向下查看下一个任务时变得聪明。您还可以将此作为方法添加到您的 Task 模型中,以便在任务实例上调用它。

    【讨论】:

    • 所以如果我得到一个项目的任务列表,我会遍历每一个,得到下一个任务。对于每个 next_task,请参阅前辈检查其依赖关系并从那里构建图表?确保我标记访问过的节点以避免重复(即任务 B 出现在项目列表中,但也是任务 A 的子项,因此它会被标记为已访问,因为我们已经看到它)。对吗?
    • 您的问题更多是关于递归而不是 Django - 您需要创建一个递归函数来查找子级并在没有找到子级时存在 - 如果您需要示例,请告诉我。
    • 感谢您的评论!是的,举个例子会更容易。我更像是这类事情的初学者:(
    • 查看我编辑的答案 - 它应该让您开始使用 Django 实例进行递归。否则,您应该查看一些寻路算法来解决最长的任务列表问题。希望这会有所帮助!
    • 丹尼尔,请参阅我的问题的编辑。当我们只想在图中采用一种方式时,您的解决方案非常有效。但是,更可取的是从任何节点获取完整图。
    猜你喜欢
    • 1970-01-01
    • 2012-06-03
    • 2012-10-25
    • 2023-03-13
    • 1970-01-01
    • 2012-10-16
    • 2011-10-04
    • 1970-01-01
    • 2015-09-20
    相关资源
    最近更新 更多