Access Many-to-Many field within Django template - python

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.

Related

How do I get reverse reference in Django template?

Apologies if the title doesn't make much sense. I don't quite understand what I lack in knowledge.
I have a Post and Comment models in my Django project. What I'm trying to do is list out all the Blog posts, and show NUMBER OF COMMENTS OF EACH POST. Please see my codes below.
models.py
class Blog(models.Model):
objects = models.Manager()
title = models.CharField(max_length=100, blank=True)
body = models.CharField(max_length=10000, blank=True)
created_at = models.DateField(auto_now_add=False)
class Comment(models.Model):
objects = models.Manager()
post = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name='comment')
views.py
def main_page(request):
all_blogs = Blog.objects.all()
context = {
'blog' : blog,
}
return render(request, 'main/home.html', context)
template
{% for b in blog %}
<div>
<p>{{b.title}}</p>
<div>
{{WHERE THE NUMBER OF THIS POST'S COMMENTS IS DISPLAYED}}
</div>
</div>
{% endfor %}
All I need is the number of the comments, but have no idea how to do it. Is there a way to make this possible in the template? Or do I have to add some codes in views.py?
You can annotate the Blog objects with the number of related Comments with:
from django.db.models import Count
def main_page(request):
all_blogs = Blog.objects.annotate(
num_comments=Count('comment')
)
context = {
'blogs' : blogs
}
return render(request, 'main/home.html', context)
The Blog objects that arise from that queryset will have an extra attribute .num_comments with the number of related comments:
{% for blog in blogs %}
<div>
<p>{{ blog.title }}</p>
<div>
{{ blog.num_comments }}
</div>
</div>
{% endfor %}

Django: Problem with Understanding Objects in For Loop

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.

Filter on many-to-one model

I want to be able assign users vehicle registrations, and when the user logs into the website their home page should have a list of vehicle regs they own.
However I cannot get the model class to filter on the foreign key.
views.py
#login_required
def home(request):
# This is the basic user landing Page
veh_list = Vehicle.objects.filter(UserProfile.user)
return render(request, 'portal/home.html', {"veh_list": veh_list})
model.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class UserProfile(models.Model):
# This line is required. Links UserProfile to a User model instance.
user = models.OneToOneField(User)
# The additional attributes we wish to include.
compName = models.CharField(max_length = 20)
milkco = models.IntegerField()
# Override the __unicode__() method to return out something meaningful!
def __unicode__(self):
return self.user.username
class Vehicle(models.Model):
vehid = models.CharField(max_length = 10)
company = models.ForeignKey(UserProfile, default = 1)
class Meta:
db_table = "vehicle"
def __unicode__(self):
return self.vehid
home.html
<!DOCTYPE html>
{% extends 'portal/base.html' %}
{% block title %}{{user.first_name }} {{ user.last_name }} Portal{% endblock %}
{% block content %}
<p>This is the home Page</p>
{% if user.is_authenticated %}
<h4>Optiload says... hello {{ user.first_name }} {{ user.last_name }}!</h4>
{% endif %}
{% for veh in veh_list %}
{{ veh}}
{% endfor %}
{% if user.is_authenticated %}
Logout<br/>
{% endif %}
{% endblock %}
Could someone help me with where I'm going wrong?
Thanks
Django offers a powerful and intuitive way to “follow” relationships in lookups, taking care of the SQL JOINs for you automatically, behind the scenes. To span a relationship, just use the field name of related fields across models, separated by double underscores, until you get to the field you want.
So your query should look like
veh_list = Vehicle.objects.filter(company__user=request.user)
Reference
You cannot make queries with unnamed arguments:
Vehicle.objects.filter(UserProfile.user)
In this case you want:
Vehicle.objects.filter(company=[your user])

django templates help,upside down

I am trying to build my blog using Django 1.8 however I do not know how can I order the blogs. See the image
I want to display the 'earliest' at the bottom and the 'latest' at the top. Here is my
index.html
{% extends 'layouts/base.html' %}
{% block title %}
Homepage - {{ block.super }}
{% endblock title %}
{% block content %}
<center>
{% for blog in blogs %}
<h2><a href="{% url 'blog_detail' slug=blog.slug %}">
{{ blog.name }}
</a></h2>
<p>{{ blog.description }}</p>
{% endfor %}
</center>
{% endblock content %}
models.py
# -*- coding: utf-8 -*-
from django.db import models
from django.utils import timezone
class blog(models.Model):
name = models.CharField(max_length=255)
description = models.TextField()
slug = models.SlugField(unique=True)
date_time = models.DateTimeField(auto_now_add = True)
def __unicode__(self):
return self.name
def get_image_path(instance, filename):
return '/'.join(['blog_images', instance.bupimg.slug, filename])
class Upload(models.Model):
bupimg = models.ForeignKey(blog, related_name="uploads")
image = models.ImageField(upload_to=get_image_path)
views.py
from django.shortcuts import render
from blogging.models import *
def index(request):
blogs = blog.objects.all()
return render(request, 'index.html', {
'blogs':blogs,
})
def blog_detail(request, slug):
article = blog.objects.get(slug=slug)
uploads = article.uploads.all()
return render(request, 'blogs/blog_detail.html', {
'article': article,
'uploads': uploads,
})
How can I move the blog title 'earliest' to the downside ,'latest' on top side? I need let the latest blog shows on the top.
You are not sorting the blogs, they come in a random order. Try changing the line
blogs = blog.objects.all()
to
blogs = blog.objects.order_by('-date_time')
The minus (-) denotes descending sort, ie. from the latest to the oldest.

Showing one-to-many relationship in Django views

I am making a django blog and want to show a list of comments for each blog post, but I have trouble figuring out how to reference the comments in the views and the templates.
My models are defined like this:
class Issue(models.Model):
title = models.CharField(max_length=255)
text = models.TextField()
author = models.ForeignKey(User)
def __unicode__(self):
return self.title
class Comment(models.Model):
commenter = models.ForeignKey(User)
issue = models.ForeignKey(Issue)
text = models.TextField()
and my views like this
class IssueDetail(DetailView):
model = Issue
context_object_name = "issue"
template_name = "issue_detail.html"
def get_context_data(self, **kwargs):
context = super(IssueDetail, self).get_context_data(**kwargs)
context['comments'] = Comment.objects.all()
return context
class CommentDetail(DetailView):
model = Comment
context_object_name = "comment"
template_name = "comment_detail.html"
and finally the issue_detail.html template
{% block content %}
<h2>{{ issue.title }}</h2>
<br/>
<i>As written by {{ issue.author.first_name }}</i>
<br/><br/>
<blockquote> {{ issue.text }}</blockquote>
<h3>Comments</h3>
{% for comment in comments %}
<li>{{comment}}</li>
{% endfor %}
{% endblock %}
This allows me to reference the fields of the comment inside the Issue template, but basically then I want the comments to have a template of their own that will be rendered inside the for loop. What is the correct way to do this in Django?
The comments are already available in your template because of the model relationship you defined. You can delete the get_context_data in IssueDetail.
Your issue_detail.html template could look like this:
{% for comment in issue.comment_set.all %}
{% include 'comment_detail.html' %}
{% endfor %}
Your comment_detail.html template could look like this:
<ul>
<li>{{ comment.issue }}</li>
<li>{{ comment.text }}</li>
</ul>
what if this we were using a different model
product = models.ForeignKey(Customer)
how would we do the CRUD opertions from the templates an the views.py

Categories

Resources