tree structure of parent child relation in django templates

烂漫一生 提交于 2019-12-02 17:47:58
DTing

from Django while loop question and

http://docs.djangoproject.com/en/dev/howto/custom-template-tags/#inclusion-tags

# view.py

@register.inclusion_tag('children.html')
def children_tag(person):
    children = person.children.all()
    return {'children': children}

# children.html

<ul>
    {% for child in children %}
    <li> <a href="{{ child.get_absolute_url }}">{{ child }}</a></li>
        {% if child.children.count > 0 %}
        {% children_list child %}
        {% endif %}
    {% endfor %}
</ul>


# your template

{% children_tag parent %}

I just finished implementing this. I wanted a tree structure for a sub-navigation, but I did not want to do anything strange with recursive templates.

The solution I implemented is very simple: I simply recurse in the view (in my case a generic helper function) and flatten out the hierarchical structure into a simple list. Then, in my template I just use a for loop to iterate over the list.

Each element in the list can be one of three things: "in", the object, or "out". In my case, I'm constructing a series of ul li elements in the view, so when I encounter "in" I create a new ul, when I encounter "out" I close the ul. Otherwise, I render the item.

My template code looks like this:

                          {% for item in sub_nav %}     
                                {% if item == "in" %}         
                                    <ul>   
                                {% else %}                    
                                    {% if item == "out" %}            
                                            </ul>                 
                                        </li>                 
                                    {% else %}                    

                                            <li>                          
                                                <a href='{{item.full_url}}'>{{item.name}}</a>
                                                {% if item.leaf %}        
                                                </li>                     
                                                {% endif %}           

                                    {% endif %}           
                                {% endif %}           
                            {% endfor %}   

The code in the helper function looks like this:

def get_category_nav(request,categories=None):
"""Recursively build a list of product categories. The resulting list is meant to be iterated over in a view"""
if categories==None:
    #get the root categories
    categories = ProductCategory.objects.filter(parent=None)
    categories[0].active=True
else:
    yield 'in'

for category in categories:
    yield category
    subcats = ProductCategory.objects.select_related().filter(parent=category)
    if len(subcats):
        category.leaf=False
        for x in get_category_nav(request,subcats):
            yield x
    else:
        category.leaf=True
yield 'out'

Using those snippets, you should be able to build any sort of hierarchical tree you'd like without doing any recursion in the template, and keeping all the logic in the view.

I know there was already an accepted answer for this, but I thought I'd post the technique in case it helps anyone else.

These are great answers but I consolidated a bit and put it on the actual model.

class RecursiveThing(models.Model):

    name = models.CharField(max_length=32)
    parent = models.ForeignKey('self', related_name='children', blank=True, null=True)

    def as_tree(self):
        children = list(self.children.all())
        branch = bool(children)
        yield branch, self
        for child in children:
            for next in child.as_tree():
                yield next
        yield branch, None

And then in your template:

<ul>
    {% for thing in things %}
        {% for branch, obj in thing.as_tree %}
            {% if obj %}
                <li>{{ obj.name }}
                {% if branch %}
                    <ul>
                {% else %}
                    </li>
                {% endif %}
            {% else %}
                {% if branch %}
                    </ul>
                {% endif %}
            {% endif %}
        {% endfor %}
    {% endfor %}
</ul>

It's very simple

All you have to do in your view is get all objects:

people = Person.objects.all()

Then in your template :

{% for person in people %}
  <li>- {{person.name}} </li>
  {% for child in person.children.all %}
     <ul>* {{child.nom}} </ul>
  {% endfor %}
 </li>          
{% endfor %}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!