Why the ListView not showing data in the template?

Question:

I’m working on my first Django project (the final project for codecademy’s Django class) and I’m making webpages to show the inventory and menu that a restaurant has. I made the model, view, template, etc. for inventory and it displays the ListView perfectly. I did the same thing for my menu and it doesn’t work. The page loads but the table that’s supposed to output data is empty.

Any insight on what might be going wrong?

PS I’m new to programming and this is my first stackoverflow post so forgive any formatting errors or other faux pas

## views.py

from django.http import HttpResponse
from django.shortcuts import render
from .models import Inventory, MenuItem, RecipeRequirement, Purchase
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from django.views.generic import ListView

# Create your views here.
def index(request):
    return render(request, "index.html")

class InventoryList(ListView):
    template_name = "inventory.html"
    model = Inventory

class MenuList(ListView):
    template_name = "menu.html"
    model = MenuItem

Inventory (below) works fine! 🙂

{% extends './base.html' %}
{% block content %}
<h2>Inventory</h2>
<table id="inventory">
  <tr>
    <th>Ingredient</th>
    <th>Price</th>
    <th>Units Available</th>
  </tr>
  {% for ingredient in inventory_list %}
  <tr>
  <tr>
      <td>{{ ingredient.ingredient_name }}</td>
      <td>{{ ingredient.price }}</td>
      <td>{{ ingredient.units_avail }}</td>
  </tr>
  {% endfor %}
</table>
{% endblock %}

This one (Menu) is the problematic one 🙁

{% extends './base.html' %}
{% block content %}
<h2>Menu</h2>
<table id="menu">
  <tr>
    <th>Item</th>
    <th>Price</th>
    <th>In Stock?</th>
  </tr>
  {% for food in menu_list %}
  <tr>
  <tr>
      <td>{{ food.menu_item_name }}</td>
      <td>{{ food.price }}</td>
      <td>{{ food.available }}</td>
  </tr>
  {% endfor %}
</table>
{% endblock %}

Models below

from django.db import models
from django.forms import DateTimeField

# Create your models here.
class Inventory(models.Model):
    ingredient_name = models.CharField(max_length=30)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    units_avail = models.IntegerField()

    def __str__(self):
        return self.ingredient_name + " avail: " + str(self.units_avail)

class MenuItem(models.Model):
    menu_item_name = models.CharField(max_length=100)
    price = models.DecimalField(max_digits=5, decimal_places=2)

    def __str__(self):
        return self.menu_item_name + " with Price: " + str(self.price)

    def available(self):
        return all(recipe_req.enough() for recipe_req in self.reciperequirement_set.all())


class RecipeRequirement(models.Model):
    ingredient = models.ForeignKey(Inventory, on_delete=models.CASCADE)
    menu_item = models.ForeignKey(MenuItem, on_delete=models.CASCADE)
    quantity = models.IntegerField()

    def __str__(self):
        return self.ingredient.ingredient_name + " in " + self.menu_item.menu_item_name

    def enough(self):
        return self.quantity <= self.ingredient.units_avail

class Purchase(models.Model):
    menu_item = models.ForeignKey(MenuItem, on_delete=models.CASCADE)
    timestamp = models.DateTimeField()

    def __str__(self):
        return self.menu_item.menu_item_name + " at " + self.timestamp

URLs below

from django.urls import path
from . import views

urlpatterns = [
    path("", views.index, name="index"),
    path("inventory", views.InventoryList.as_view(), name="inventory_list"),
    path("menu", views.MenuList.as_view(), name="menu_list"),
]
Asked By: shinobi

||

Answers:

I could see one problem, you have one extra <tr> tag open in both the templates just below the loop.

Also think some problem with available method you manually defined in MenuItem model, so I removed it, try without it.

Also I’d recommend you to use context_object_name which is by default object_list so:

views.py

class InventoryList(ListView):
    template_name = "inventory.html"
    model = Inventory
    context_object_name="inventories"

class MenuList(ListView):
    template_name = "menu.html"
    model = MenuItem
    context_object_name="menu_list"

inventory.html template

{% extends './base.html' %}
{% block content %}
<h2>Inventory</h2>
<table id="inventory">
  <tr>
    <th>Ingredient</th>
    <th>Price</th>
    <th>Units Available</th>
  </tr>
  {% for ingredient in inventories %}
  <tr>
      <td>{{ ingredient.ingredient_name }}</td>
      <td>{{ ingredient.price }}</td>
      <td>{{ ingredient.units_avail }}</td>
  </tr>
  {% endfor %}
</table>
{% endblock %}

menu.html template

{% extends './base.html' %}
{% block content %}
<h2>Menu</h2>
<table id="menu">
  <tr>
    <th>Item</th>
    <th>Price</th>
    <th>In Stock?</th>
  </tr>
  {% for food in menu_list %}
  <tr>
      <td>{{ food.menu_item_name }}</td>
      <td>{{ food.price }}</td>
  </tr>
  {% endfor %}
</table>
{% endblock %}

Note: It’s recommended to give / at the end of every route so your urls.py should be:

urls.py

from django.urls import path
from . import views

urlpatterns = [
    path("", views.index, name="index"),
    path("inventory/", views.InventoryList.as_view(), name="inventory_list"),
    path("menu/", views.MenuList.as_view(), name="menu_list"),
]

Note: class based views in Django requires their name to be written as model name as prefix and actual view name as suffix, so you may change it to InventoryListView and MenuItemListView from InventoryList and MenuList respectively.

Answered By: Sunderam Dubey

I notice that the RecipeRequirement model Class has the method available. This method calls the method enough from the RecipeRequirement model Class.

One troubleshooting strategy is to disable (comment out) the methods enough and available as they are introducing logic that would prevent display of menu items. In other words, your code appears to read: If we don’t have enough ingredients there is no point returning menu items for recipes that we cannot make.

An alternative troubleshooting strategy is to populate the RecipeRequirement table with sufficient ingredients and it should work. Caveat: This answer is untested.

Here’s a tip for a newcomer to coding that has served me well in my brief journey into software development. Errors have been my greatest teacher. When there are errors try to reduce as many elements as you can. When you hit a wall like this consider what is essential and comment out the rest. I suggested commenting out the two methods first because they are not required for the tables to display (or for what you report as being central to your question). Once you get the menu items displaying then you can re-introduce those methods one at time.

Answered By: Carewen