Tree Structure (Foreign Keys to itself) and templa

2019-08-21 08:18发布

问题:

I have a tree structure for Categories. Categories with a Foreign key that is referring to itself.

class Category(MetaData):
    parent = models.ForeignKey('self', blank=True, null=True, verbose_name='parent category', on_delete=models.CASCADE)
    name = models.CharField(max_length=255)
    description = models.TextField()

Because I don't know the depth of the categories tree(can't use for) I need to use a recursive function:

def cat_for_parents(self, cat_obj):
                ...
                if cat_obj.parent_id:
                        p = cat_obj.parent
                        ...
                        self.cat_for_parents(p)

But how I implement the function in the template to get something like this(to theoretically infinite recursive loop):

<ul>
 <li>CategoryA
  <ul>
   <li>SubCategA1
     <ul>
       <li> Subcateg Subcateg B1
         <ul>
            <li> Subcateg SubCateg C1>
             <li> Subcateg SubCateg C2>
         <ul>
         <li> Subcateg Subcateg B2>
         ............. 

回答1:

I used inclusion_tag to solve this.

Example:

model:

#just add related_name:
parent = models.ForeignKey('self', blank=True, null=True, related_name='subs', on_delete=models.CASCADE)

views.py:

categories = Category.objects.filter(parent=None)
# then pass it to template

template:

<ul>
    {% for category in categories %}
        <li>
            {{ category.name }}
            {% if category.parent.count > 0 %}
                {% tree_structure category %}
            {% endif %}
        </li>
    {% endfor %}
</ul>

Custom tag function:

@register.inclusion_tag('path/to/tree_structure.html')
def tree_structure(category):
    subs = category.subs.all()
    return {"subs": subs}

tree_structure.html:

# remember to load your custom tags file
<ul>
    {% for sub in subs %}
        <li>
            {{ sub.name }}
            {% if sub.parent.count > 0 %}
                {% tree_structure sub %}
            {% endif %}
        </li>
    {% endfor %}
</ul>

How it works:

Getting the categories without any parent and send them to template. In template we use a for loop to render categories one by one and before going to next category to render, you check if that category hast any subs. If there was any subs for that category, you will pass the category to your custom template tag and there you will get all the sub categories for that given category and pass it to another template file to render them but before finishing it, check for that category to see if that one has sub categories too or not and if it does just call custom function again before u finish rendering the first one. it will goes till there is no more category and sub categories and at the end passes a complete template with all the sub categories for the first passed category to the main template to render before other categories.

I couldn't test this code myself so there might be small issues.

Also check the documentation for custom template tags: Custom template tags and filters