Django: Problem with Understanding Objects in For Loop

Question:

I am a beginner in Django. I am building a Django app, named PhoneReview. It will store reviews related to the latest mobile phone. It will also display phone brands, along with the associated phone models. I have managed to do some protion of the app. Right now, I am a bit confused with a line of code.

I have a code like this in one of my template files:

{% extends 'gamereview/base.html' %}

{% block title%}
Detail
{% endblock %}


{% block content %}

<h3>This is the review for {{game.title}} </h3>

<ul>{% for review_item in game.review_set.all %}
    <li>{{ review_item.review }}</li>
    {% endfor %}
</ul>

{% endblock %}

I don’t understand this portion:

<ul>{% for review_item in game.review_set.all %}

What does this line mean?

Here are the codes in models.py:

from django.db import models
from django.template.defaultfilters import slugify

# Create your models here.
class Tag(models.Model):
    label = models.CharField(max_length=20)

    def __str__(self):
        return self.label

class Game(models.Model):
    title = models.CharField(max_length=100)
    developer = models.CharField(max_length=100)
    platform = models.CharField(max_length=50, default='null')
    label_tag = models.ManyToManyField(Tag)
    slug = models.SlugField(max_length=150, default='null')

    def __str__(self):
        return self.title

    def save(self, *args, **kwargs):
        self.slug = slugify(self.title)
        super().save(*args, **kwargs)

class Review(models.Model):
    game = models.ForeignKey(Game, on_delete=models.CASCADE)
    review = models.CharField(max_length=1000)
    date = models.DateField(auto_now=True)
    slug = models.SlugField(max_length=150, default='null')

    def __str__(self):
        return self.review

    def save(self):
        super(Review, self).save()
        self.slug = '%i-%s' % (
            self.id, slugify(self.game.title)
        )
        super(Review, self).save()

Here are the codes in views.py:

from django.views import generic
from .models import Game


class GameListView(generic.ListView):
    template_name = 'gamereview/gamelist.html'
    context_object_name = 'all_games'

    def get_queryset(self):
        return Game.objects.all()

class ReviewView(generic.DetailView):
    model = Game
    template_name = 'gamereview/review.html'

Here are the codes in urls.py:

from . import views
from django.urls import path

app_name = 'gamereview'

urlpatterns = [
    path('gamereview/', views.GameListView.as_view(), name='gamelist'),
    path('gamereview/<slug:slug>/', views.ReviewView.as_view(), name='review'),

]

I am a bit confused by this line: <ul>{% for review_item in game.review_set.all %}. Would you please help me to clarify?

Asked By: Shawn

||

Answers:

Look at the models; there is a Game class. Apparently you receive an instance of that class in your template under the name game.

The Game class is referenced as a foreign key by Review class. This, due to the way Django ORM works, gives Game class a reverse link .review_set; it contains all review objects that refer to the particular game. Hence game.review_set.

That .review_set attribute is not a list but a DB result set. You can filter it, sort it, etc, but in your case you just fetch all the records from it. Hence game.review_set.all.

Please take some time to read an intro to how Django works, a number of things there cannot be derived from mere common sense.

Answered By: 9000

I’d like to add something that was helpful for me when referencing the related database result set:

In your class, you can specify a "related_name" for the foreign key and use it to reference that result set in your template.

For instance,

class Review(models.Model):
    game = models.ForeignKey(Game, on_delete=models.CASCADE, related_name="game_reviews")

And, now you reference this in your template (assuming the context name is all_games):

{% for x in all_games %}

  {% all_games.title %} </br>

<ul>{% for review_item in game.game_reviews.all %}
    <li>{{ review_item.review }}</li>
    {% endfor %}
</ul>

{% endfor %}

Of course, you can further simplify the related_name to just "reviews" and shorten your code that much more. And, it’s all very logical when you read it.

Answered By: cbirch
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.