Django中使用Bootstrap展示树形结构

Posted

tags:

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

概述

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

实现

模型类(models.py)

  1. class Department(models.Model):
  2. name = models.CharField(u‘单位名称‘, max_length=30)
  3. pri = models.IntegerField(u‘序号‘)
  4. desc = models.CharField(u‘备注‘, max_length=100)
  5. parent = models.ForeignKey(‘self‘, blank=True, null=True, related_name=‘children‘, verbose_name=‘上级单位‘)
  6. full_path = models.CharField(u‘全路径‘, max_length=20, blank=True, null=True)
  7. deep_level = models.IntegerField(u‘深度‘, default=0)
  8. def __str__(self):
  9. return self.name
  10. class Meta:
  11. verbose_name = u‘组织结构‘
  12. verbose_name_plural = u‘组织结构‘
  13. class People(models.Model):
  14. PEOPLE_TYPE_CHOICES = (
  15. (‘A‘, u‘公务员‘),
  16. (‘B‘, u‘事业编‘),
  17. (‘C‘, u‘合同工‘),
  18. (‘D‘, u‘临时工‘),
  19. )
  20. user = models.OneToOneField(User)
  21. nickname = models.CharField(u‘昵称‘, max_length=50)
  22. job = models.CharField(u‘职务‘, max_length=50)
  23. job_type = models.CharField(u‘类别‘, choices=PEOPLE_TYPE_CHOICES, max_length=2, default=‘A‘)
  24. department = models.ForeignKey(Department, related_name=‘peoples‘, verbose_name=‘单位‘)
  25. def __str__(self):
  26. return self.nickname
  27. class Meta:
  28. verbose_name = u‘人员‘
  29. verbose_name_plural = u‘人员‘
  30. class TreeNode():
  31. def __init__(self):
  32. self.id = 0
  33. self.text = "Node 1"
  34. self.href = "#node-1"
  35. self.selectable = True
  36. self.state = {
  37. ‘checked‘: True,
  38. ‘disabled‘: True,
  39. ‘expanded‘: True,
  40. ‘selected‘: True,
  41. },
  42. self.tags = [‘available‘],
  43. self.nodes = []
  44. def to_dict(self):
  45. icon = (len(self.nodes) > 0) and ‘glyphicon glyphicon-list-alt‘ or ‘glyphicon glyphicon-user‘
  46. return {
  47. ‘id‘: self.id,
  48. ‘text‘: self.text,
  49. ‘icon‘: icon,
  50. ‘href‘: self.href,
  51. ‘tags‘: [‘1‘],
  52. ‘nodes‘: self.nodes,
  53. }

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

  1. p = Department.objects.get(parent=None)
  2. 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)

  1. from django.conf.urls import patterns, url
  2. from dept import views as dept_vies
  3. urlpatterns = [
  4. url(r‘^show/$‘, dept_vies.show, name=‘dept_show‘),
  5. url(r‘^tree/$‘, dept_vies.tree, name=‘dept_tree‘),
  6. url(r‘^people/(\d+)$‘, dept_vies.people, name=‘dept_people‘),
  7. ]

视图(views.py)

  1. def get_dept_tree(parents):
  2. ‘‘‘
  3. 根据提供的父节点,迭代出所有的子节点,并用一个dict的列表来表示
  4. :param parents 父节点列表:
  5. :return 返回dict列表:
  6. ‘‘‘
  7. display_tree = []
  8. for p in parents:
  9. node = TreeNode()
  10. node.id = p.id
  11. node.text = p.name
  12. children = p.children.all()
  13. if len(children) > 0:
  14. node.nodes = get_dept_tree(children)
  15. display_tree.append(node.to_dict())
  16. return display_tree
  17. def show(request):
  18. return render(request, "dept/show.html")
  19. def tree(request):
  20. root = Department.objects.get(parent=None)
  21. tree = get_dept_tree([root])
  22. return JsonResponse(tree, safe=False)
  23. def people(request, pk):
  24. dept = Department.objects.get(pk=pk)
  25. peoples = dept.peoples.all()
  26. return render(request, "dept/peoples.html", {‘peoples‘: peoples})

模板类

show.html

  1. {% extends ‘base.html‘ %}
  2. {% block title %}组织结构{% endblock %}
  3. {% block head %}
  4. <link type="text/css" href="/static/css/bootstrap-treeview.min.css">
  5. {% endblock %}
  6. {% block script %}
  7. <script type="text/javascript" src="/static/js/bootstrap-treeview.min.js"></script>
  8. <script type="text/javascript">
  9. $(function () {
  10. $.ajaxSetup({
  11. data: {csrfmiddlewaretoken: ‘{{ csrf_token }}‘ },
  12. });
  13. var tree = $.getJSON("{% url ‘dept_tree‘ %}", ‘‘, function (data) {
  14. $(‘#tree‘).treeview({
  15. data: data,
  16. level: 2,
  17. showTags: true,
  18. onNodeSelected: function (event, node) {
  19. $.post("../people/"+node.id, {}, function(data){
  20. console.log(data);
  21. $("#result").html(data);
  22. })
  23. }
  24. });
  25. });
  26. });
  27. </script>
  28. {% endblock %}
  29. {% block content %}
  30. <hr>
  31. <div class="row">
  32. <div id="tree" class="col-md-3"></div>
  33. <div id="result" class="col-md-9">
  34. sss
  35. </div>
  36. </div>
  37. {% endblock %}

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

  1. $.ajaxSetup({
  2. data: {csrfmiddlewaretoken: ‘{{ csrf_token }}‘ },
  3. });

Bootstrap Tree View的使用文档见GitHub

people.html

  1. <table class="table table-bordered">
  2. <tr>
  3. <th>姓名</th><th>职务</th><th>类型</th>
  4. </tr>
  5. {% for people in peoples %}
  6. <tr>
  7. <th>{{ people.nickname }}</th>
  8. <th>{{ people.job }}</th>
  9. <th>{{ people.get_job_type_display }}</th>
  10. </tr>
  11. {% empty %}
  12. <tr><th colspan="3">没有人员</th> </tr>
  13. {% endfor %}
  14. </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目前还没有较好的办法。





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

使用ztree展示树形菜单结构

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

VUE展示无限层级树形数据结构

Oracle查询直观展示树形结构数据

Bootstrap树形菜单插件TreeView js使用方法详解

树形展示三级分类并分页:递归树形结构