程序设计过程中,我们常常用树形结构来表征某些数据的关联关系,如企业上下级部门、栏目结构、商品分类等等,通常而言,这些树状结构需要借助于数据库完成持久化。理想中树形结构应该具备如下特征:数据存储冗余度小、直观性强;检索遍历过程简单高效;节点增删改查CRUD操作高效。网上看到有两种方案:一种是直观而简单的设计思路,另一种是基于左右值编码的改进方案。感兴趣的可以查看树形结构的数据库表设计,接下来将根据这篇文章的数据讲下我的设计。
一、基本数据
本文列举了一个食品族谱的例子进行讲解,通过类别、颜色和品种组织食品,树形结构图如下:
二、基于索引链的Schema设计
原理很简单,每个节点都存储自身之上(包含自身)所有节点的关系链,上述数据可以描述为如下图所示:
node_id | name | level | index_link |
1 | Food | 0 | 1- |
2 | Fruit | 1 | 1-2- |
3 | Meat | 1 | 1-3- |
4 | Red | 2 | 1-2-4- |
5 | Yellow | 2 | 1-2-5- |
6 | Beef | 2 | 1-3-6- |
7 | Pork | 2 | 1-3-7 |
8 | Cherry | 3 | 1-2-4-8- |
9 | Banana | 3 | 1-2-5-9- |
三、实现代码
from django.db import models
class FoodModel(models.Model):
name = models.CharField(max_length=64)
level = models.SmallIntegerField()
parent_id = models.IntegerField()
index_link = models.CharField(max_length=255)
@classmethod
def create(cls, name, parent_id=None):
if parent_id:
parent = cls.objects.get(id=parent_id)
level = parent.level + 1
food= cls.objects.create(parent_id=parent_id, level=level, name=name)
food.index_link = parent.index_link + '%d-' % food.id
food.save()
else:
food= cls.objects.create(parent_id=0, level=0, name=name)
food.index_link = '%d-' % food.id
food.save()
return food
def get_parent_nodes(self, level=None):
assert not level or level < self.level
parent_id_list = self.index_link.split('-')[:-2]
parent_nodes = FoodModel.objects.filter(id__in=parent_id_list)
if level:
parent_nodes = parent_nodes.filter(level=level)
return parent_nodes
def get_child_nodes(self, level=None):
assert not level or level > self.level
child_nodes = FoodModel.objects.filter(index_link__contains=self.index_link)
if level:
child_nodes = child_nodes.filter(level=level)
return child_nodes