Django PostgreSQL – 高效获取递归类别结构

问题描述 投票:0回答:2

我有一个看起来像这样的模型:

class Category(models.Model):
    name = models.CharField(max_length=50)
    slug = models.SlugField()
    parent = models.ForeignKey(
        'categories.Category',
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        related_name='categories'
    )

基本上,在

parent
字段中,它引用自己。如果父级设置为 None,则它是根类别。

我用它来构建类别层次结构。

什么是最有效的方法:

  1. 通过层次结构获取所有对象
  2. 在模板中显示它们?

出于某种原因,

select_related
似乎并没有导致这里的性能改进。

我还发现了这个:How to recursively query in django efficiently?

但是很难将它应用到我的示例中,因为我仍然不太明白发生了什么。这是我的结果:

    WITH RECURSIVE hierarchy(slug, parent_id) AS (
        SELECT slug, parent_id 
        FROM categories_category
        WHERE parent_id = '18000'

        UNION ALL

        SELECT sm.slug, sm.parent_id
        FROM categories_category AS sm, hierarchy AS h
        WHERE sm.parent_id = h.slug
        )
    SELECT * FROM hierarchy

不胜感激任何帮助。

谢谢!

django postgresql django-models django-orm
2个回答
0
投票

一个可能的解决方案可以使用https://django-mptt.readthedocs.io/en/latest/overview.html#what-is-django-mptt

MPTT 是一种在数据库中存储分层数据的技术。这 目的是使检索操作非常有效。 这种效率的权衡是在树周围执行插入和移动项目更加复杂,因为需要一些额外的工作来始终保持树结构处于良好状态。

from django.db import models
from mptt.models import MPTTModel, TreeForeignKey

class Category(MPTTModel):
    name = models.CharField(max_length=50)
    slug = models.SlugField()
    parent = TreeForeignKey(
        'self',
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        related_name='children'
    )

    class MPTTMeta:
        order_insertion_by = ['name']

您可以像这样使用 django-mptt 模板标签:

{% load mptt_tags %}
<ul>
    {% recursetree categories %}
        <li>
            {{ node.name }}
            {% if not node.is_leaf_node %}
                <ul class="children">
                    {{ children }}
                </ul>
            {% endif %}
        </li>
    {% endrecursetree %}
</ul>

图书馆文档中有教程和更多信息。


0
投票

我遇到了同样的问题,最终创建了以下函数,该函数访问数据库一次,然后整理出层次结构并返回一个字典:

def get_category_tree():
    categories = Category.objects.order_by('name')
    itemtree = {}
    # Add 'children' attribute to each category; populate dict
    for category in categories:
        category.children = {}
        itemtree[category.pk] = category
    # Add categories to 'children'
    for key,value in itemtree.items():
        if value.parent_id:
            itemtree[value.parent_id].children[key] = value
    # Return top-level items
    return {k:v for k,v in itemtree.items() if not v.parent_id}

返回的字典的每个

value
都是一个顶级类别对象,它有一个
children
属性。

您可以通过遍历字典值在模板中呈现它。以下示例将处理三个级别的层次结构:

<ul>
{% for level1 in category_tree.values %}
    <li>
        {{ level1.name }}
        {% if level1.children %}
        <ul>
            {for level2 in level1.children.values %}
            <li>{{ level2.name }}
                {% if level2.children %}
                <ul>
                    {for level3 in level2.children.values %}
                    <li>{{ level3.name }}</li>
                    {% endfor %}
                </ul>
                {% endif %}
            </li>
            {% endfor %}
        </ul>
        {% endif %}
    </li>
{% endfor %}
</ul>

如果你需要渲染多层次的层次结构,你可以考虑使用模板递归。阅读以下问题和答案以确定是否合适:Represent a tree of objects in Django template

© www.soinside.com 2019 - 2024. All rights reserved.