Django中树形结构

Posted DevOps

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django中树形结构相关的知识,希望对你有一定的参考价值。

概述

在企业管理系统中,经常会有树形结构需求,例如:组织结构、权限等等,本文使用Django和Bootstrap Tree View来展示企业组织结构和对应组织的人员。

实现

模型类(models.py)

class Department(models.Model):

    name = models.CharField(u‘单位名称‘, max_length=30)

    pri = models.IntegerField(u‘序号‘)

    desc = models.CharField(u‘备注‘, max_length=100)

    parent = models.ForeignKey(‘self‘, blank=True, null=True, related_name=‘children‘, verbose_name=‘上级单位‘)

    full_path = models.CharField(u‘全路径‘, max_length=20, blank=True, null=True)

    deep_level = models.IntegerField(u‘深度‘, default=0)

    def __str__(self):

        return self.name

    class Meta:

        verbose_name = u‘组织结构‘

  verbose_name_plural = u‘组织结构‘

class People(models.Model):

    PEOPLE_TYPE_CHOICES = (

        (‘A‘, u‘公务员‘),

        (‘B‘, u‘事业编‘),

        (‘C‘, u‘合同工‘),

        (‘D‘, u‘临时工‘),

    )

    user = models.OneToOneField(User)

    nickname = models.CharField(u‘昵称‘, max_length=50)

    job = models.CharField(u‘职务‘, max_length=50)

    job_type = models.CharField(u‘类别‘, choices=PEOPLE_TYPE_CHOICES, max_length=2, default=‘A‘)

    department = models.ForeignKey(Department, related_name=‘peoples‘, verbose_name=‘单位‘)

    def __str__(self):

        return self.nickname

    class Meta:

        verbose_name = u‘人员‘

  verbose_name_plural = u‘人员‘

class TreeNode():

    def __init__(self):

        self.id = 0

  self.text = "Node 1"

  self.href = "#node-1"

  self.selectable = True

  self.state = {

                         ‘checked‘: True,

                         ‘disabled‘: True,

                         ‘expanded‘: True,

                         ‘selected‘: True,

                     },

        self.tags = [‘available‘],

        self.nodes = []

    def to_dict(self):

        icon = (len(self.nodes) > 0) and ‘glyphicon glyphicon-list-alt‘ or ‘glyphicon glyphicon-user‘

  return {

            ‘id‘: self.id,

            ‘text‘: self.text,

            ‘icon‘: icon,

            ‘href‘: self.href,

            ‘tags‘: [‘1‘],

            ‘nodes‘: self.nodes,

        }

Department为组织结构,为表示上下级关系,使用了自关联,ForeignKey的第一个参数为self 
,为方便上级查找所有的下级,添加related_name=’children’,这样在查询指定单位的下级时候,可以使用:


p = Department.objects.get(parent=None)

children = p.children.all()


来查询所有的下级。 
People类为单位人员,我将它关联到Django内建的User,实现User和People的一对一关联,可以在后台管理页面直接编辑People,People类有一个外键,指向Department,同样添加了related_name=’peoples’方便查询。 
为显示树状结构,我建立了一个TreeNode类,这个类不需要保存到数据库,它的属性主要供Bootstrap Tree View使用,因为Python的json模块不能直接序列化TreeNode类,所以添加一个to_dict方法,先将TreeNode转化为一个dict。

URL映射(urls.py)

from django.conf.urls import patterns, url

from dept import views as dept_vies

urlpatterns = [

    url(r‘^show/$‘, dept_vies.show, name=‘dept_show‘),

    url(r‘^tree/$‘, dept_vies.tree, name=‘dept_tree‘),

    url(r‘^people/(\d+)$‘, dept_vies.people, name=‘dept_people‘),

]


视图(views.py)

def get_dept_tree(parents):

    ‘‘‘

    根据提供的父节点,迭代出所有的子节点,并用一个dict的列表来表示

    :param parents 父节点列表:

    :return 返回dict列表:

    ‘‘‘

    display_tree = []

    for p in parents:

        node = TreeNode()

        node.id = p.id

        node.text = p.name

        children = p.children.all()

        if len(children) > 0:

            node.nodes = get_dept_tree(children)

        display_tree.append(node.to_dict())

    return display_tree

def show(request):

    return render(request, "dept/show.html")

def tree(request):

    root = Department.objects.get(parent=None)

    tree = get_dept_tree([root])

    return JsonResponse(tree, safe=False)

def people(request, pk):

    dept = Department.objects.get(pk=pk)

    peoples = dept.peoples.all()

    return render(request, "dept/peoples.html", {‘peoples‘: peoples})

模板类

show.html

{% extends ‘base.html‘ %}

{% block title %}组织结构{% endblock %}

{% block head %}

    <link type="text/css" href="/static/css/bootstrap-treeview.min.css">

{% endblock %}

{% block script %}

    <script type="text/javascript" ></script>

    <script type="text/javascript">

        $(function () {

            $.ajaxSetup({

                data: {csrfmiddlewaretoken: ‘{{ csrf_token }}‘ },

            });

            var tree = $.getJSON("{% url ‘dept_tree‘ %}", ‘‘, function (data) {

                $(‘#tree‘).treeview({

                    data: data,

                    level: 2,

                    showTags: true,

                    onNodeSelected: function (event, node) {

                        $.post("../people/"+node.id, {}, function(data){

                            console.log(data);

                            $("#result").html(data);

                        })

                    }

                });

            });

        });

    </script>

{% endblock %}

{% block content %}

    <hr>

    <div class="row">

        <div id="tree" class="col-md-3"></div>

        <div id="result" class="col-md-9">

            sss

        </div>

    </div>

{% endblock %}

Django 中自带了防止CSRF攻击的功能,GET请求不需要 CSRF 认证,POST 请求需要正确认证才能得到正确的返回结果,如果没有认证,常常会出现下面django csrf token missing or incorrect的错误。最简洁的方法时设置JQuery的ajax属性:

$.ajaxSetup({

    data: {csrfmiddlewaretoken: ‘{{ csrf_token }}‘ },

});

Bootstrap Tree View的使用文档见【https://github.com/jonmiles/bootstrap-treeview】

people.html

<table class="table table-bordered">

    <tr>

        <th>姓名</th><th>职务</th><th>类型</th>

    </tr>

    {% for people in peoples %}

    <tr>

        <th>{{ people.nickname }}</th>

        <th>{{ people.job }}</th>

        <th>{{ people.get_job_type_display }}</th>

    </tr>

        {% empty %}

        <tr><th colspan="3">没有人员</th> </tr>

    {% endfor %}

</table>

在show.html页面,当点击不同的节点时候,会发出形如“people/1”的ajax请求,返回结果是一段HTML代码,通过JQuery的html()方法动态加载到show.html页面上。 
people有一个job_type属性,为一个元组,如果直接使用people.job_type,仅仅会显示A、B、C等结构,如果想显示对应的描述信息,需要使用get_job_type_display。

总结

本文基本实现树形结构的展示、一对多关联查询、数据异步加载等功能,但具体细节还不够完善。 
树形结构每个单位右边的角标没有实现,仅仅用了一个固定数字1来占位,设想可以显示单位人数,待完善。 
树形结构的查询需要进一步封装,如果要查询根节点下的所有单位,目前只能够多次查询,查询效率不高,考虑使用full_path和deep_level两个冗余字段存储单位的全路径和节点深度,通过startswith来查询所有子节点,如果使用SQL SERVER数据库,可以通过触发器的方式动态修改这两个字段你,使用Django目前还没有较好的办法。



欢迎关注运维自研堂订阅号,运维自研堂是一个技术分享平台,主要是运维自动化开发:linux、python、django、saltstack、tornado、bootstrap、redis、golang、docker、etcd等经验分享。


开源    创新     共享


投稿&商务合作

Mail:idevops168@163.com       QQ:785249378


牛人并不可怕,可怕的是牛人比我们还努力!



以上是关于Django中树形结构的主要内容,如果未能解决你的问题,请参考以下文章

python django 业务树形结构规划及页面渲染

django-树形结构

系统管理之组织机构树形化结构优化篇

Python入门自学进阶-Web框架——17Django实现评论树形结构Model操作

Python入门自学进阶-Web框架——17Django实现评论树形结构Model操作

如何在 Django Summernote 中显示编程片段的代码块?