Django: Problem with Understanding Objects in For Loop - python

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?

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.

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.

Related

Reverse related object lookup ( _set )is not accessing objects

I am Building a BlogApp and I stuck on a Problem. I am trying to access two model objects but Failed many times.
models.py
class Topic(models.Model):
topic_no = models.CharField(max_length=100,default='')
topic_title = models.CharField(max_length=200,default='')
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(Profile,on_delete=models.CASCADE,null=True)
def __str__(self):
return self.dairy_title
class Entry(models.Model):
topic = models.ForeignKey(Topic, on_delete=models.CASCADE,default='',related_name='topic')
date_added = models.DateTimeField(auto_now_add=True,null=True)
updated = models.DateTimeField(auto_now=True)
note = models.TextField()
def __str__(self):
return str(self.topic)
views.py
def show_entry(request):
showd = Entry.objects.all()
context = {'showd':showd}
return render(request ,'mains/showd.html', context)
showd.html
{% for post in topic.journel_set.all %}
{{ post.topic_title }}
{{ post.note }}
{% endfor %}
The Problem
I am trying to access both model's Objects in showd.html.
What have i tried
I saw tons of Answers like :- This This and Many More answers about reverse related object lookup. BUT nothing is worked for me.
I don't know am i doing wrong in this.
Any help would be Appreciated.
Thank You in Advance.
Entry has ForeignKey towards Topic it is not reverse accessor so Entry has only one Topic
So you could for instance do
{% for entry in showd %}
{{ entry.topic.topic_title }}
{{ entry.note }}
{% endfor %}

How do i query for comment in Django

I am new in Django and I have been following tutorials online. I am having problem on how to display the comments.
How do i query for comments in views, so i can display comments for a particular post.
Model:
class Post(models.Model):
poster_profile = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, blank=True,null=True)
image_caption = models.TextField(blank=True, null=True)
class Comments (models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, blank=True,null=True)
commented_image = models.ForeignKey(Post, on_delete=models.CASCADE, null=True, blank=True)
comment_post = models.TextField()
Views.py:
def home_view(request):
comment = Comments.objects.all() #This is getting all comment in all post, how do i query for comment in a particular post.
context{'comment':comment}
return render(...)
Template:
{% for com in comment %}
<p>{{ com.comment_post }}</p>
{% endfor %}
You can do
post = Post.objects.get(id=1)
comment = post.comments_set.all()
Following Relationships "Backward"
If a model has a ForeignKey, instances of the foreign-key model will have access to a Manager that returns all instances of the first model. By default, this Manager is named FOO_set, where FOO is the source model name, lowercased. This Manager returns QuerySets, which can be filtered and manipulated as described in the “Retrieving objects” section above.
Note this behaviour can be overridden.
You can override the FOO_set name by setting the related_name parameter in the ForeignKey definition. For example, if the Entry model was altered to blog = ForeignKey(Blog, on_delete=models.CASCADE, related_name='entries'), the above example code would look like this:
Edit #2:
views.py:
def home_view(request):
posts = Post.objects.all().reverse()[5]
context{ 'posts': posts, }
return render(...)
Now in your templates you can do something like:
{% if posts %}
{% for post in posts %}
{{ post.image_caption }}
{% for comment in post.comments_set.all %}
{{ comment.comment_post }}
{% endfor %}
{% endfor %}
{% endif %}
First of all, take a look at Django queryset documentation, especially select_related for this kind of issues (to reduce number of queries to database). I didn't try but following snippet must work.
class Post(models.Model):
poster_profile = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
image_caption = models.TextField(blank=True, null=True)
class Comments (models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
commented_image = models.ForeignKey(Post, related_nam="comments", on_delete=models.CASCADE)
comment_post = models.TextField()
...
def home_view(request):
post = Post.objects.filter(id=request.data.get('post_id')).select_related("comments") # specify the post anyhow ..
comments = post.comments
context{'comment': comments}
return render(...)
def home_view(request):
particular_Post= Post.objects.get(id=1)
comment = Comments.objects.get(Post=particular_Post)
context{'comment':comment}
return render(...)
to understund Query in django i suggest U to start by
python manage.py shell
and Import you're Models

Using the 'for' loop in django tags for 'ForeignKey'

My django loop in the templates does not work correctly. Why, because of me, everything looks good?
Any help will be appreciated.
My models.py
class Tags(models.Model):
name = models.CharField(max_length=10)
class Person(models.Model):
keywords = models.ForeignKey(Tags, on_delete=models.CASCADE)
My views.py
def person_detail(request, user_id):
person = get_object_or_404(Person, pk=user_id)
context = {'person': person}
return render(request, 'person_detail.html', context)
My templates.html
{% for tag in person.tags %}
<span class="badge badge-lg badge-pill badge-info"># {{ person.tags.name } </span>
{% endfor %}
This gives no results even though there is data in the database.
You are doing it backwards. Your Tags model should have a foreign key to Person, not the other way around. For example:
class Person(models.Model):
...
class Tag(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
name = models.CharField(max_length=10)
Note that I've also renamed your Tags model to Tag as it makes sense to name models singular.
Then you can write the following:
{% for tag in person.tag_set.all %}
...
{% endfor %}

Access Many-to-Many field within Django template

I have the following blog project :
models.py:
from django.db import models
from django.utils import timezone
class Post(models.Model):
title = models.CharField(max_length=200)
body = models.TextField()
pub_date = models.DateTimeField(default=timezone.now)
categories = models.ManyToManyField('Category')
def __str__(self):
return self.title
class Category(models.Model):
title = models.CharField(max_length=200)
def __str__(self):
return self.title
# Change the name in Admin from categorys to categories
class Meta:
verbose_name_plural = "categories"
views.py:
from django.shortcuts import render
from .models import Post, Category, Comment
def getPosts(request):
posting = Post.objects.all().order_by('-pub_date')
categories = Category.objects.all()
context = {
'posting':posting,
'categories':categories,
}
return render(request, 'posts/getPosts.html', context)
getPosts.html template :
{% if posting %}
{% for article in posting %}
<h3>{{article.title}}</h3>
<ul>{{article.body}}</ul>
<ul>Posted : {{article.pub_date}}</ul>
<ul>
<em>Found in category : </em>
{{ article.categories }}
{{ article.categories.all }}
{% for category in categories %}
{{category.title}}
{% endfor %}
</ul>
{% endfor %}
{% endif %}
I have three posts, which all display properly, but
{{article.categories}} is giving me:
posts.Category.None
{{article.categories.all}} gives me
QuerySet [<Category: Diving>]
And the second loop outputs the list of all categories, which I expected as just a test run:
Kit & Packing Diving Places Tips Private
I am trying to simply pull through the category name for each post, which has been selected in the admin panel and saved through the admin panel.
I have tried what feels like a thousand different suggestions, such as changing the view to category = post.category_set.all(), and have been researching this for days now, but am getting no-where.
You already have the right answer; article.categories.all, which you should loop over.
{% for category in article.categories.all %}
{{category.title}}
{% endfor %}
You don't need the categories value in the view at all.

Django listview with multiple models and filters

Django newbie here...
I have three models which I'm currently wrestling with and I obtained a desired solution. However, the solution is certainly not efficient nor good practice. I just don't know enough to adequately implement what I want.
Here are the models...
basics/models.py
class Tag(models.Model):
slug = models.SlugField(max_length=200, unique=True)
def __unicode__(self):
return self.slug
def get_absolute_url(self):
return reverse("tag_index", kwargs={"slug": self.slug})
class post(models.Model):
title = models.CharField(max_length = 180)
// a bunch of other fields here
tags = models.ManyToManyField(Tag)
def get_absolute_url(self):
return reverse("post_detail", kwargs={"slug": self.slug})
def __unicode__(self):
return self.title
class Meta:
verbose_name = "Blog Post"
verbose_name_plural = "Blog Posts"
ordering = ["-created"]
projects/models.py
class project(models.Model):
projectTitle = models.CharField(max_length=150, null=False)
projectTag = models.OneToOneField(Tag, primary_key=True)
description = MarkdownField()
def __unicode__(self): # __unicode__ on Python 2
return self.projectTitle
class Meta:
verbose_name = "Project"
verbose_name_plural = "Projects"
Now... what I would like to do is to create an adequate view, which passes to my template only the posts which are tagged with the project tags rather than all posts as it is currently doing.
projects/views.py
class projectFeed(generic.ListView):
queryset = project.objects.all()
template_name = "projects.html"
paginate_by = 5
def get_context_data(self, **kwargs):
context = super(projectFeed, self).get_context_data(**kwargs)
# slug = self.kwargs['projectTag']
# tag = Tag.objects.get(slug=slug)
context['posts'] = post.objects.all()#filter(tags=tag)
return context
As you can see, I tried some stuff that I used for creating the view for displaying all posts with a certain tag but I couldn't get it to work here.
projects/templates/projects.html
{% for project in project_list %}
<div class='postContent'>
<!-- project stuff here -->
{% for post in posts %}
{% if project.projectTag in post.tags.all %}
<p> {{ post.title }} </p>
{% endif %}
{% endfor %}
</div>
{% endfor %}
Ideally... I want to pass a nested list of sorts where each project in the project_list has an associated list of posts which I can iterate over in the inner for loop without needing to check the tags. That is, I want to filter and arrange the data in the view method/class OR another more efficient place.
You can get list of related post using next relate managers:
posts = project.projectTag.post_set.all()
Why won't you try using it in your template
{% for project in project_list %}
<!-- project stuff hete -->
{% for post in project.projectTag.post_set.all %}
<!-- post stuff goes here -->
{% endfor %}
{% endfor %}
But to reduce number of sql queries you will have to override get_queryset method of your ListFilter:
def get_queryset(self):
return super(projectFeed, self).get_queryset()
.select_related('projecttag')
.prefetch_related('projecttag__post_set')

Categories

Resources