简体   繁体   中英

Django templates: data filtering by foreign key

I'm new to Django and I have a problem I can't seem to solve. Long story short, I created a text based app that helps me create a meal plan and generates a shopping list. And I'm trying to recreated with django.

Here are my models:

class Recipe(models.Model):
    name = models.CharField(max_length=40)

    def __str__(self):
        return self.name


class Category(models.Model):
    name = models.CharField(max_length=20)

    def __str__(self):
        return self.name


class Ingredient(models.Model):
    name = models.CharField(max_length=20)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)

    def __str__(self):
        return self.name


class IngredientSet(models.Model):
    recipe = models.ForeignKey(Recipe, on_delete=models.SET_NULL, null=True)
    ingredient = models.ForeignKey(Ingredient, on_delete=models.DO_NOTHING)
    quantity = models.FloatField()

    def __str__(self):
        return self.ingredient.name

Now, on my List Views I want to display the names of stored recipes as links. Each recipe link should call a Detail View which will display selected recipe's sets of ingredients. I can't figure out how to access those by their foreign key(which points to a recipe).

One can access reverse relations by using the model name in lowercase with _set appended. For Recipe and IngredientSet you can write recipe.ingredientset_set.all() to get all the instances of IngredientSet related to a recipe. One can customize this name by setting the related_name eg:

class IngredientSet(models.Model):
    recipe = models.ForeignKey(Recipe, on_delete=models.SET_NULL, null=True, related_name="ingredient_sets")
    ingredient = models.ForeignKey(Ingredient, on_delete=models.DO_NOTHING)
    quantity = models.FloatField()

    def __str__(self):
        return self.ingredient.name

Now you can write recipe.ingredient_sets.all() .

To loop over this in the template one can simply write:

{% for ingredient_set in recipe.ingredient_sets.all %}
    {{ ingredient_set.ingredient.name }}
    {{ ingredient_set.quantity }}
{% endfor %}

Note : This will make a query to the database for each recipe to get it's ingredient set. You can use prefetch_related [Django docs] if you want to reduce the amount of queries made.

assuming that you want to display the names of stored recipes in a html template as links:

views.py

from .models import Recipe, Ingredient, IngredientSet
from django.shortcuts import render

def recipes(request):
    recipes_for_template = Recipe.objects.all()
    return render(request, 'main/recipes.html', {"recipes": recipes_for_template})

def ingredients(request, cod_recipe):
    ingredients_sets = IngredientSet.objects.filter(recipe=cod_recipe)
    ingredients_to_template = []
    for ingredient_set in ingredients_sets:
        ingredient_obj = Ingredient.objects.get(id=ingredient_set.id)
        dict_of_ingredient = {
            "name": ingredient_obj.name,
            "quantity": ingredient_set.quantity
        }
        ingredients_to_template.append(dict_of_ingredient)
    return render(request, 'main/ingredients.html', {"ingredients": ingredients_to_template})

where main is the name of the app

recipes.html

<table>
    <thead>
        <th>Recipe</th>
    </thead>
    <tbody>
        {% for recipe in recipes %}
        <tr>
            <td>
                <a href="./{{recipe.id}}/">{{recipe.id}}</a>
            </td>
        </tr>
        {% endfor %}
    </tbody>
</table>

ingredients.html

{% for ingredient in ingredients %}
    {{ingredient.name}}
    {{ingredient.quantity}}
{% endfor %}

urls.py

from django.urls import path

from . import views


app_name = "main"

urlpatterns = [
    path('recipe/', views.recipes, name='recipes'),
    path('recipe/<int:cod_recipe>/', views.ingredients, name='ingredients'),
]

I think this is it, any doubts i will be glad to answer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM