Django html template getting data from nested dictionary

Question:

I’m having a problem with an html template not displaying model fields sent from a view in a context dictionary called content. This dictionary holds a nested dictionary like:

content = {'indredients': {recipe id1: QuerySet 1,
                           recipe id2: QuerySet 2, ... }          } 

In model.py:

class Ingredients(models.Model):
    id = models.IntegerField(primary_key=True)
    recipe = models.ForeignKey(Recipes, on_delete=models.CASCADE, related_name='ingredients')
    ingredient = models.CharField(max_length=128)

    class Meta:
        managed = False
        db_table = 'ingredients'
        verbose_name_plural = 'Ingredients'

    def __str__(self):
        return f"{self.id} {self.recipe} - {self.ingredient}"

class Recipes(models.Model):
    id = models.IntegerField(primary_key=True)
    category = models.TextField(db_column='Category', null=False)
    submitted_by = models.TextField(
        db_column='Submitted_By', null=False)
    origin = models.TextField(db_column='Origin', null=False)
    title = models.TextField(db_column='Title', null=False)
    directions = models.TextField(
        db_column='Directions', null=False)
    comments = models.TextField(db_column='Comments', null=False)
    created = models.DateTimeField(null=False)
    modified = models.DateTimeField(null=True)

    def __str__(self):
        return f"{self.id} - {self.title}"

    class Meta:
        managed = False
        db_table = 'recipes'
        verbose_name_plural = 'Recipes'

In views.py:

    recipes = Recipes.objects.all().order_by(
        "category", "title")

    content['ingredients'] = {}
    for recipe in recipes:
        ingredients = Ingredients.objects.filter(
            recipe=recipe.id).order_by("id")
        content['ingredients'][recipe.id] = ingredients
    content['recipes'] = recipes

    return render(
        request,
        "myApp/recipes.html",
        context=content)

In recipes.html:

{% for recipe in recipes %}
    <div id="id-{{recipe.id}}" class="grid-item {{recipe.category}} {{recipe.submitted_by}}">
        <div class="row">
            <div class="col-12 col-md-3 ingredients">
                {% for queryQbject in ingredients.recipe.id %}
                    {{ queryQbject.ingredient }}<br>
                {% empty %}
                    <span>No ingredients provided</span>
                {% endfor %}
            </div>
    </div>
{% endfor %}

I do get the correct data from the sqlite database and the Queryset is stored in the dictionary ‘content’ that is passed correctly into the html file. However, the html template doesn’t display any of the data and only prints the ‘No ingredients provided’ {% empty %} case.

See debug info:

Debug info showing content of the dictionary 'content'

What do I need to do to fix this problem?

Asked By: Volker Petersen

||

Answers:

You are trying to loop over the ID, which is an integer. Not an iterable.
Change

{% for queryQbject in ingredients.recipe.id %}

To

{% for queryQbject in ingredients.recipe %}
Answered By: nigel239

nigel239’s answer got me thinking and researching some more. I found this post
https://fedingo.com/how-to-lookup-dictionary-value-with-key-in-django-template/
to write a custom filter to lookup a dictionary value with a key.

This is my custom_tags.py:

@register.filter
def get_item(dictionary, key):
    try:
        key = int(key)
        value = dictionary.get(key)
    except:
        value = None
    return value

and my updated recipes.html:

<div class="col-12 col-md-3 ingredients">
    {% for queryset in ingredients|get_item:recipe.id %}
        {{ queryset.ingredient }}<br>
    {% empty %}
        <span>No ingredients provided</span>
    {% endfor %}
</div>

Now the code correctly pulls all the ingredients from the Django Queryset that was passed into the html template in a dictionary called ‘ingredients’ using the ‘recipe.id’ as keys.

Answered By: Volker Petersen