2

同じクラスへの ForeignKey を持つ Django モデルがあり、効果的にツリーを作成します。

class Tag(models.Model):
    name = models.CharField(max_length=50)
    parent = models.ForeignKey('self', blank=True, null=True)

Django シェル ( ./manage.py shell) で再帰をいじってみると、ツリーをプレーン テキストとして簡単に表すことができます。

def nodes(parent, level):
    children = Tag.objects.filter(parent=parent)
    for c in children:
        spaces = ""
        for i in xrange(0,level):
            spaces+="  "
        print "%s%s" % (spaces,c.name)
        nodes(c.pk,level+1)

nodes(None,0)

よくわからないのは、ツリー全体を Django テンプレートに入れる方法です。これを簡単にするためにカスタム テンプレート タグを作成しましたが、データをテンプレートに渡してツリーを簡単に反復処理してテンプレートに表示する方法がわかりません。これが基本的なテンプレートタグです。

@register.inclusion_tag("core/tags.html")
def render_tags(**kwargs):
    tags = Tag.objects.all()
    return {"tags":tags}

上記が非常に基本的なものであることは知っていますが、ここからどこに行くべきかわかりません。Tag クラスに子を取得する関数があればもっと簡単かもしれないと思ったので、クラスにも持っています:

    def children(self):
        return Tag.objects.filter(parent=self.pk)

私はself.pkそこで使用します。ツリーのルートは単純ですrootTag=Tag()。保存されていないためpkがないrootTag.children()ため、親タグを持たないタグが見つかり、これらのタグのいずれかでchildren()関数が呼び出され続けることができます. しかし、私が言ったように、これを何らかの単一のデータ構造に変換してテンプレートに渡す方法がわかりません。

考え?私はおそらく一種の辞書を作成したいと思いますが、ここではフォローできません。

4

2 に答える 2

9

Jproffittが述べたように、 Django MPTTは、目的を達成するための優れた方法です。

それを使用すると、次のように、テンプレートで子インスタンスに再帰的にアクセスできます。

{% load mptt_tags %}
<h1>Tags</h1>
<ul>
{% recursetree tags %}
    <li>{{ node.name }}
        {% if not node.is_leaf_node %}
            <ul>
                {{ children }}
            </ul>
        {% endif %}
    </li>
{% endrecursetree %}
</ul>

プロジェクトの 1 つに使用していますが、セットアップも使用も簡単です。

サードパーティ アプリを使用しない代替ソリューション

専用アプリを使用したくない場合は、これらのソリューションが機能する可能性があると思います (未テスト):

# yourapp/tags_list.html
<ul>
{% for tag in tags %}
    <li>{{ tag.name }}</li>
    {% if tag.children.exists %}
       {% with tag.children.all as tags %}
            {% include "yourapp/tags_list.html" %}
        {% endwith %}    
    {% endif %}
{% endfor %}
</ul>

このように、子タグがなくなるまで、テンプレートは自分自身を再帰的に呼び出す必要があります。このソリューションでは、タグ モデルで関連する名前を指定する必要があります。

parent = models.ForeignKey('self', blank=True, null=True, related_name="children")

ただし、これらのソリューションには、MPTT を使用するよりも多くのデータベース クエリが含まれることに注意してください。

于 2013-10-05T07:47:01.933 に答える
6

調べれば調べるほど、テンプレートの再帰を使用してこれを実行しようとするのは適切ではないことに気付きましたが、今のところ django-mptt は避けたいと思います。単純な問題であり、非常に大きなツリーを作成する予定はありません。

この関連する質問の回答では、テンプレートの再帰は実際には使用すべきではなく、代わりにビューでシリアライゼーションを処理するべきであることが指摘されています。元の質問で述べたように、辞書を作成する必要がある可能性が最も高いと仮定して、これはより良いと思っていました。

この答えは基本的に私が探していたものです。これserializable_objectはテンプレートに渡すことができるものであり、後でjsonを使用することにした場合は、その回答に記載されているように非常に簡単です。

django-mptt はおそらくここで行う「最も正しい」ものであるため、エリオットの回答を正しいものとして選択しますが、これは別のサードパーティアプリを避けたい人のための代替ソリューションです。

class Tag(models.Model):
    name = models.CharField(max_length=50)
    description = models.CharField(max_length=100, blank=True)
    parent = models.ForeignKey('self', blank=True, null=True)

    # Not necessary, can use Tag.tag_set.all()
    # But this function uses pk which allows
    #    rootTag=Tag() 
    #    rootTag.children()
    # Because rootTag has no pk
    def children(self):
        return Tag.objects.filter(parent=self.pk)

    def serializable_object(self):
        obj = {'name': self.name, 'children': []}
        for child in self.children():
            obj['children'].append(child.serializable_object())
        return obj

そのモデルでは、次のいずれかを実行できます。

rootTag=Tag()
rootTag.serializable_object()

または:

tags = []
for t in Tag.objects.filter(parent=None):
    tags.append(t.serializable_object())

# And if you need JSON, 
from django.utils import simplejson
simplejson.dumps(tags)
于 2013-10-05T16:44:25.247 に答える