Django中分层结构的创建与管理
立即解锁
发布时间: 2025-08-22 01:25:46 阅读量: 1 订阅数: 2 


Django Web开发实战秘籍
### Django中分层结构的创建与管理
在开发论坛、评论系统或分类系统时,我们常常需要在数据库中存储分层结构的数据。虽然关系型数据库(如MySQL或PostgreSQL)的表是扁平的,但有一种快速有效的方法可以存储分层结构,即Modified Preorder Tree Traversal(MPTT)。MPTT允许我们在不进行数据库递归调用的情况下读取树结构。下面我们将详细介绍如何在Django中使用MPTT来处理分层结构。
#### 1. MPTT工作原理
想象将树水平展开,根节点在顶部。树中的每个节点都有左右值,就像节点左右两侧的小手柄。从根节点开始逆时针遍历树,并为遇到的每个左右值标记一个数字(1, 2, 3...)。在数据库表中,每个节点有标题、左值和右值。
- **获取子树**:若要获取左值为2、右值为11的节点B的子树,需选择左值在2到11之间的所有节点,如C、D、E和F。
- **获取祖先节点**:要获取左值为5、右值为10的节点D的所有祖先节点,需选择左值小于5且右值大于10的节点,即B和A。
- **计算后代数量**:可使用公式 `descendants = (right - left - 1) / 2` 计算节点的后代数量。例如,节点B的后代数量为 `(11 - 2 - 1) / 2 = 4`。
- **节点操作**:若要将节点E附加到节点C,只需更新它们的第一个共同祖先节点B的左右值。
为了更方便地处理这些操作,Django提供了 `django-mptt` 应用,它处理这些算法并提供简单的API来管理树结构。Django CMS也使用 `django-mptt` 来管理网页。
#### 2. 创建分层类别
为了说明如何使用MPTT,我们将创建一个电影应用,其中包含分层的 `Category` 模型和与类别具有多对多关系的 `Movie` 模型。
##### 2.1 准备工作
- 安装 `django-mptt`:在虚拟环境中运行以下命令:
```bash
(myproject_env)$ pip install django-mptt
```
- 创建 `movies` 应用,并将 `movies` 和 `mptt` 添加到 `INSTALLED_APPS` 中:
```python
# settings.py
INSTALLED_APPS = (
# ...
"mptt",
"movies",
)
```
##### 2.2 创建模型
- **Category模型**:打开 `models.py` 文件,添加一个继承自 `mptt.models.MPTTModel` 和 `CreationModificationDateMixin` 的 `Category` 模型。除了混合类的字段外,`Category` 模型还需要一个 `TreeForeignKey` 类型的 `parent` 字段和一个 `title` 字段。
```python
# movies/models.py
# -*- coding: UTF-8 -*-
from django.db import models
from django.utils.translation import ugettext_lazy as _
from utils.models import CreationModificationDateMixin
from mptt.models import MPTTModel
from mptt.fields import TreeForeignKey, TreeManyToManyField
class Category(MPTTModel, CreationModificationDateMixin):
parent = TreeForeignKey("self", blank=True, null=True)
title = models.CharField(_("Title"), max_length=200)
def __unicode__(self):
return self.title
class Meta:
ordering = ["tree_id", "lft"]
verbose_name = _("Category")
verbose_name_plural = _("Categories")
```
- **Movie模型**:创建一个继承自 `CreationModificationDateMixin` 的 `Movie` 模型,并包含一个 `title` 字段和一个 `TreeManyToManyField` 类型的 `categories` 字段。
```python
class Movie(CreationModificationDateMixin):
title = models.CharField(_("Title"), max_length=255)
categories = TreeManyToManyField(Category,
verbose_name=_("Categories"))
def __unicode__(self):
return self.title
class Meta:
verbose_name = _("Movie")
verbose_name_plural = _("Movies")
```
##### 2.3 MPTTModel的作用
`MPTTModel` 混合类会为 `Category` 模型添加 `tree_id`、`lft`、`rght` 和 `level` 字段。
- `tree_id`:用于区分数据库表中的多个树,每个根类别存储在单独的树中。
- `lft` 和 `rght`:存储MPTT算法中使用的左右值。
- `level`:存储节点在树中的深度,根节点的级别为0。
此外,`MPTTModel` 混合类还添加了一些方法来导航树结构,类似于使用JavaScript导航DOM元素。以下是一些常用方法:
| 方法 | 描述 |
| --- | --- |
| `get_ancestors(ascending=False, include_self=False)` | 获取类别祖先节点 |
| `get_root()` | 获取根类别 |
| `get_children()` | 获取类别直接子节点 |
| `get_descendants(include_self=False)` | 获取类别所有后代节点 |
| `get_descendant_count()` | 获取类别后代数量 |
| `get_siblings(include_self=False)` | 获取类别所有兄弟节点 |
| `get_previous_sibling()` | 获取类别上一个兄弟节点 |
| `get_next_sibling()` | 获取类别下一个兄弟节点 |
| `is_root_node()` | 检查类别是否为根节点 |
| `is_child_node()` | 检查类别是否为子节点 |
| `is_leaf_node()` | 检查类别是否为叶子节点 |
#### 3. 创建类别管理界面
`django-mptt` 应用提供了一个简单的模型管理混合类,允许我们创建树结构并缩进显示。为了对树进行重新排序,我们可以使用第三方解决方案,如 `django-mptt-admin` 和 `django-mptt-tree-editor`。
##### 3.1 使用 `django-mptt-admin`
- **准备工作**:
- 安装 `django-mptt-admin`:在虚拟环境中运行以下命令:
```bash
(myproject_env)$ pip install django-mptt-admin
```
- 将 `django_mptt_admin` 添加到 `INSTALLED_APPS` 中:
```python
# settings.py
INSTALLED_APPS = (
# ...
"django_mptt_admin"
)
```
- **创建管理界面**:创建一个继承自 `DjangoMpttAdmin` 的 `CategoryAdmin` 类。
```python
# movies/admin.py
# -*- coding: UTF-8 -*-
from django.contrib import admin
from django_mptt_admin.admin import DjangoMpttAdmin
from models import Category
class CategoryAdmin(DjangoMpttAdmin):
list_display = ["title", "created", "modified"]
list_filter = ["created"]
admin.site.register(Category, CategoryAdmin)
```
- **工作原理**:类别管理界面有树视图和网格视图两种模式。树视图使用 `jqTree` jQuery库进行节点操作,可以展开和折叠类别,通过拖放标题来重新排序或更改依赖关系。网格视图显示默认的类别更改列表,可用于过滤、排序或应用管理操作。
##### 3.2 使用 `django-mptt-tree-editor`
- **准备工作**:
- 安装
0
0
复制全文
相关推荐










