Nested loop in Django template

24,531

Solution 1

You can use backwards relationships.

{% for item in ingrcat %}
<h2>{{ item.name }}</h2>
<ul>
    {% for ingr in item.ingredience_set.all %}
    <li><a href="#" id="i{{ ingr.id }}">{{ ingr.name }}</a></li>
    {% endfor %}
</ul>
{% endfor %}

See documentation:

https://docs.djangoproject.com/en/dev/topics/db/queries/#following-relationships-backward

Solution 2

Do get_queryset on obj.manytomanyfield

{% for item in ingrcat %}
<h2>{{ item.name }}</h2>
<ul>
    {% for ingr in item.ingredients.get_queryset %}
    <li><a href="#" id="i{{ ingr.id }}">{{ ingr.name }}</a></li>
    {% endfor %}
</ul>
{% endfor %}
Share:
24,531

Related videos on Youtube

finspin
Author by

finspin

Updated on January 22, 2021

Comments

  • finspin
    finspin almost 3 years

    I can't get my head around this. I need to somehow access the object in the parent loop but I'm not sure how. Here is what I've come up with so far. I marked the problematic area in the code with XXX:

    Template:

    {% for item in ingrcat %}
        <h2>{{ item.name }}</h2>
        <ul>
            {% for ingr in XXX %}
            <li><a href="#" id="i{{ ingr.id }}">{{ ingr.name }}</a></li>
            {% endfor %}
        </ul>
    {% endfor %}
    

    XXX - should be a list of ingredients belonging to the ingredience category which is currently being looped through in the parent loop.

    View:

    def home(request):
        if request.user.is_authenticated():
            username = request.user.username
            email = request.user.email
            foods = Food.objects.filter(user=request.user).order_by('name')
            ingredients = Ingredience.objects.filter(user=request.user).order_by('name')
            ingrcat = IngredienceCategory.objects.filter(user=request.user)
    
            context = {}
            for i in ingredients:
                context[i.category.name.lower()] = context.get(i.category.name.lower(), []) + [i]
    
            newcontext = {'foods': foods, 'ingredients': ingredients, 'ingrcat': ingrcat, 'username': username, 'email': email,}
        else:
            context = {}
            newcontext = {}
    
        context = dict(context.items() + newcontext.items())
    
        return render_to_response('home.html', context, context_instance=RequestContext(request))
    

    Models:

    from django.db import models
    from django.contrib.auth.models import User
    
    class IngredienceCategory(models.Model):
        name = models.CharField(max_length=30)
        user = models.ForeignKey(User, null=True, blank=True)
    
        class Meta:
            verbose_name_plural = "Ingredience Categories"
    
        def __unicode__(self):
            return self.name
    
    
    class Ingredience(models.Model):
        name = models.CharField(max_length=30)
        category = models.ForeignKey(IngredienceCategory, null=True, blank=True)
        user = models.ForeignKey(User, null=True, blank=True)
    
        class Meta:
             verbose_name_plural = "Ingredients"
    
        def __unicode__(self):
            return self.name
    
    
    class Food(models.Model):
        name = models.CharField(max_length=30)
        ingredients = models.ManyToManyField(Ingredience)
        html_id = models.CharField(max_length=30, null=True, blank=True)
        user = models.ForeignKey(User, null=True, blank=True)
    
        class Meta:
            verbose_name_plural = "Foods"
    
        def __unicode__(self):
            return self.name
    
    • rantanplan
      rantanplan about 11 years
      You need to include the model definitions, in order to see the relationships between them.
    • finspin
      finspin about 11 years
      Right. Models are now included.
  • finspin
    finspin about 11 years
    Thanks for the pointer, I didn't know you could go backwards like that! However, I'm getting template error Could not parse the remainder: '()' from 'item.ingredience_set.all()'. I tried some variations of ingredience_set but couldn't make it work.
  • Fozzle
    Fozzle about 11 years
    Ah yes, I was mistaken. In templates you don't use (). {% for ingr in item.ingredience_set.all %} should do the trick.