Search code examples
djangodjango-viewsdjango-templatesdjango-template-filters

Django for loop in template


I have two datasets:

ButikPages = ShopPages.objects.filter(UserID_id=User, Parent_id__isnull=True, IsActive=1, URL=shop_page_slug)
SubPages = ShopPages.objects.filter(UserID_id=User, Parent_id__isnull=False, URL=shop_page_slug)

In my Template I try to filter these two lists that if one ButikPages Query has SubPages it should a drop down menu if not a link menu.

<ul class="navbar-nav">
    {% for page in ButikPages %}
    {% for parentpage in SubPages %}
    {% if page.IsActive == 1 and parentpage.Parent_id != page.ID %}
       <li class="nav-item"><a class="nav-link" href="/{{ page.Slug }}/">{{ page.PageTitle }}</a></li>
       {% else %}
       <li class="nav-item dropdown">
       <a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">{{ page.PageTitle }}</a>
       {% if parentpage.Parent_id == page.ID and parentpage.IsActive == 1 %}
       <ul class="dropdown-menu">
       <li><a class="dropdown-item" href="/{{ parentpage.Slug }}/">{{ parentpage.PageTitle }}</a></li>
       </ul>
      </li>
     {% endif %}
     {% endif %}
     {% endfor %}
     {% endfor %}
      </ul>

That works if a Menu just has one entry, but if I have two SubPages it appears two time, which is what I understand because the two for loops. But how can I access the SubPages without the second loop?

PS its easier if I just have one menu type I know it's working but the only difference is that I have two kind of menues.

regards.


Solution

  • It seems like you have built a tree-like object list in the model ShopPages. I think you should create the tree-like structure in your view first, and then display it in the template later, instead of using two for-loop directly in the template.

    Something like:

    shop_pages = ShopPages.objects.filter(UserID_id=User, IsActive=1, URL=shop_page_slug)
    parents = list(shop_pages.filter(Parent_id__isnull=True).values('pk', 'Parent_id'))
    children = list(shop_pages.filter(Parent_id__in=parents).values('pk', 'Parent_id'))
    pages = [
        {
            'pk': parent['pk'],
            'parent_id': parent['Parent_id'],
            'sub_pages': [
                {
                    'pk': child['pk'],
                    'parent_id': child['Parent_id'],
                } for child in children if child['Parent_id'] == parent['pk']
            ],
        } for parent in parents
    ]
    

    And in your template, do something like:

    {% for page in pages %}
        {{ page.pk }}
        ..
        ..
        {% for sub_page in page.sub_pages %}
            {{ sub_page.pk }}
            ..
            ..
        {% endfor %}
    {% endfor}
    

    Sorry I cannot test it now. But I found some mistakes about the position for loop of children. Maybe it works now.


    I just suddenly figure out that you can use the reverse-FK to collect the children of your parent-pages. So if you doesn't change the related_name of the FK Parent_id, its reverse field would be Parent_id_set, then you can do:

    shop_pages = ShopPages.objects.filter(UserID_id=User, IsActive=1, URL=shop_page_slug)
    

    and

    <ul>
      {% for page in shop_pages %}
      {{ page.pk }}
      ..
        {% for sub_page in page.Parent_id_set.all %}
        {{ sub_page.pk }}
        ..
        {% endfor %}
      {% endfor %}
    </ul>