Double loop in Django template - python

I have been trying to do this nested loop for couple of hours, but so far none of my results worked. Here is what i have so far:
index.html
{% for category in categories %}
<div class="col-md-12 column category list-group">
<p><b>{{ category.name }}</b></p>
{% for subcategory in subcategories %}
<div class="list-group-item">
<h5 class="mb-1">{{ subcategory.name }}</h5>
<small>{{ subcategory.description }}</small>
<small>{{ subcategory.count_threads }}</small>
</div>
{% endfor %}
</div>
{% endfor %}
views.py
class IndexView(ListView):
template_name = 'myForum/index.html'
context_object_name = 'SubCategory_list'
queryset = SubCategory.objects.all()
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['categories'] = Category.objects.all()
context['subcategories'] = SubCategory.objects.all()
return context
models.py
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class SubCategory(models.Model):
name = models.CharField(max_length=255)
description = models.CharField(max_length=255)
category = models.ForeignKey('Category', default=0)
def __str__(self):
return self.name
Output
Output
My problem is that SubCategory - News, belongs to Infromation, category Off-Topic belongs to General. For some reason loop displays all of subcategories, and i can't figure out how to narrow it to current Category.

You can change the inner loop to:
{% for subcategory in category.subcategory_set.all %}
This will loop over the subcategories for the current category.
Since you are looping over categories in the outer loop, you can change the queryset in the list view to use the Category model. You can cut down the number of queries by prefetching the subcategories.
It also looks as if you can remove the get_context_data method, since the list view already makes the queryset available in the template context.
class IndexView(ListView):
template_name = 'myForum/index.html'
context_object_name = 'catrgories'
queryset = Category.objects.all().prefetch_related('subcategory_set')

Related

Filtering through relationship in views or template

I have one simple model called Product
class Product(models.Model):
title = models.CharField(max_length=150)
def __str__(self):
return self.title
And one other model called ExternalProduct
class ExternalProduct(models.Model):
title = models.CharField(max_length=100)
external_id = models.CharField(max_length=25)
internal_product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
blank=True,
related_name='external_products',
)
price = models.IntegerField()
brand = models.ForeignKey(Brand, on_delete=models.CASCADE, blank=True)
image_url = models.CharField(max_length=255)
product_url = models.CharField(max_length=255)
store = models.CharField(max_length=50)
active = models.BooleanField(default=True)
class Meta:
ordering = ['price']
def __str__(self):
return self.title
On the detailed view of the Product, I want to display the price of all ExternalProduct related to the Product. It works all fine with this view
# products/views.py
class ProductDetailView(DetailView):
model = Product
context_object_name = 'product'
template_name = 'products/product_detail.html'
and this template
# product_detail.html
{% extends '_base.html' %}
{% block title %}{{ product.title }}{% endblock title %}
{% block content %}
<div class="book-detail">
<h2>{{ product.get_active_external_products }}</h2>
</div>
<div>
<ul>
{% for ep in product.external_products.all %}
<li>{{ ep.price }}</li>
{% endfor %}
</ul>
</div>
{% endblock content %}
The problem is that I just want to display the price of ExternalProduct which has active=True
Been trying to solve it with a custom method in products views.py
class ProductDetailView(DetailView):
model = Product
context_object_name = 'product'
template_name = 'products/product_detail.html'
def get_active_external_products(self):
return self.external_products.objects.filter(active=True)
And a modified for loop inside product_detail.html
{% for ep in product.get_active_external_products %}
<li>{{ ep.price }}</li>
{% endfor %}
But unfortunately without any success. The site does not crash, just doesn't show anything other than the rest of the html file.
Any advice?
You can use prefetch_related with Prefetch to filter only active external products. For this you need to override get_queryset() method in your view:
from django.db.models import Prefetch
class ProductDetailView(DetailView):
model = Product
context_object_name = 'product'
template_name = 'products/product_detail.html'
def get_queryset(self):
return Product.objects.prefetch_related(Prefetch("external_products", queryset=ExternalProduct.objects.filter(active=True), to_attr="active_external_products"))
Note to_attr="active_external_products" part. It tells that active external products will be available as active_external_products attribute. So in template you can get them like this:
{% for ep in product.active_external_products %}
<li>{{ ep.price }}</li>
{% endfor %}
Or you can just override get_context_data() to insert external products to the context directly:
class ProductDetailView(DetailView):
model = Product
context_object_name = 'product'
template_name = 'products/product_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(****kwargs)
context["active_external_products"] = self.object.external_products.filter(active=True)
return context
In this case in template you can use active_external_products variable name directly:
{% for ep in active_external_products %}
<li>{{ ep.price }}</li>
{% endfor %}

Setting Up Category and Forum and Loop it

I'm creating a forum software in Django and I'm having a hard time figuring out how to relate the forums table to the categories. I want to display this to the index page:
Category 1
--forum 1
--forum 2
--forum 2
Category 2
--forum 1
--forum 2
--forum 3
These are my models:
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Forum(models.Model):
name = models.CharField(max_length=255)
description = models.CharField(max_length=255)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='forums')
def __str__(self):
return self.name
Here are my views:
class HomeView(ListView):
context_object_name = 'name'
template_name = 'index.html'
def get_context_data(self, *args, **kwargs):
context = super(HomeView, self).get_context_data(*args, **kwargs)
context['forums'] = Forum.objects.all()
context['categorys'] = Category.objects.all()
return context
This is what I currently have on the home page, the only problem is, it's simply looping through all the categories and forums. I want the category to be looped in the first for loop, and in the second one to pull all the forums that belong to that category.
{% for category in categorys %}
--code
{% for forum in forums %}
--code
{% endfor %}
{% endfor %}
How do I fix this so that it displays properly and the relation is correct? A category can have many forums but a forum can only have one category. So a one-to-many relationship I believe.
~Thanks,
Taz
Its simple:
{% for category in categorys %}
{{ category.name }}
{% for forum in category.forums.all %}
{{ forum.name }}
{% endfor %}
{% endfor %}
Also, you do not need to pass forums through context variable(via get_context_data method).

Django TypeError objects is not iterable in Templates

I have two models one is club details and the other is player structure. so my plan is to based on the club iteration which has values like'test', 'odi', 'listA' i need to represent its respective player structure in my Django template.
models.py
class Clubstate(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class PlayerStructure(models.Model):
clubstate = models.ForeignKey('Clubstate', on_delete=models.CASCADE, related_name='clubstate')
country = models.ForeignKey('TeamStructure', on_delete=models.CASCADE, null=True, related_name='identifier1')
firstname = models.CharField(max_length=255)
lastname = models.CharField(max_length=255)
imageUri = models.ImageField(upload_to='images/', verbose_name='image')
JerseyNumber = models.IntegerField()
def __str__(self):
return self.firstname + self.lastname
In views.py I'm using DetailView to represent the data.
class PlayerStatistics(DetailView):
context_object_name = 'Clubstate_details'
model = models.Clubstate
template_name = 'CricketDetails/playerstatistics_details.html'
Template html
<div class="jumbotron">
<div class="container">
<div class="row">
{% for club in Clubstate_details %}
<h1>{{club.name</h1>
{% endfor %}
</div>
</div>
</div>
My thought, logic is like
{% for club in clubs %}
{{club.name}}
{{club.player_structure.firstname}}
{{club.player_structure.lastname}}
{% endfor%}
So that for indivdual club i get its respective player structure.
I get TypeError: 'Clubstate' object is not iterable error.
Hope that makes sense...
Detail view won't give a list of items, instead it gives a single view only so change the DetailView to ListView and iterate through the output
class PlayerStatistics(ListView):
context_object_name = 'Clubstate_details'
model = models.Clubstate
template_name = 'CricketDetails/playerstatistics_details.html'
if you want detail view can get it by
{{ Clubstate_details.name }}
with DetailView
FK instance can be accessed by
{% for obj in Clubstate_details.playerstructure_set.all %}
{{ obj.firstname }}
{% endfor %}

object is not shown in CategoryView

I wrote in top.html
<div>
<a href="#">
Category
</a>
<div>
{% for category in category_content %}
<a href="{% url 'category' category.name %}">
{{ category.name }}
</a>
{% endfor %}
</div>
</div>
in category.html like
<div>
{% for content in queryset %}
<h2>{{ content.title }}</h2>
SHOW DETAIL
{% endfor %}
</div>
When I put in top.html,no error happens and page is shown but no <h2>{{ content.title }}</h2> is shown here.
I wrote in views.py
class CategoryView(ListView):
model = Category
template_name = 'category.html'
def get_queryset(self):
category_name = self.kwargs['category']
self.category = Category.objects.get(name=category_name)
queryset = super().get_queryset().filter(name=self.category)
return queryset
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['categories'] = Category.objects.all()
return context
def category(self,pk):
queryset = self.base_queryset()
category = self.kwargs.get("category")
queryset = queryset.filter(category__name=category)
return queryset
in urls.py
urlpatterns = [
path('top/', views.top, name='top'),
path('category/<str:category>/',views.CategoryView.as_view(), name='category'),
]
in models.py
class Category(models.Model):
name = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
class Index(models.Model):
title = models.CharField(max_length=100)
text = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
I want to show list of Index data that are by filtered Category in my db in category.html .For example, it is my ideal system ,when I put Python link in top.html,content.title which is related with Python is shown in category.html .But now no content.title is shown.What is wrong in my code?How should I fix this?
You shoud add filtered Index list to the context first:
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['categories'] = Category.objects.all()
context['index_list'] = Index.objects.filter(category=self.category)
return context
Now index_list will be available inside template and you can do this:
<div>
{% for content in index_list %}
<h2>{{ content.title }}</h2>
SHOW DETAIL
{% endfor %}
</div>

django querysets in templates

I am trying to make specific queries by using some model entry fields.
I have the following model entry:
models.py
class Work(models.Model):
categories =(
('cat1', 'cat1'),
('cat2', 'cat2'),
('cat3', 'cat3'),
('cat4', 'cat4'),
('cat5', 'cat5'),
)
title = models.CharField(max_length=200)
description = RichTextUploadingField(config_name='awesome_ckeditor')
date = models.DateTimeField(default=timezone.now)
category = models.CharField(max_length=200, choices = categories, default = 'projects')
thumb = models.ImageField(upload_to = 'works/thumbs', blank = True)
content = models.FileField(upload_to = 'works/content_media', blank = True)
published = models.BooleanField()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("work_detail",kwargs={'pk':self.pk})
#property
def thumb_url(self):
if self.thumb and hasattr(self.thumb, 'url'):
return self.thumb.url
#property
def content_url(self):
if self.content and hasattr(self.content, 'url'):
return self.content.url
here is the view:
views.py
class WorksListView(ListView):
template_name = 'template.html'
model = Work
def get_queryset(self):
return Work.objects.filter(published=True).order_by('-date')
and I am trying to query first by the category field then by entry in the following template:
template.html
{% for category in works_list.category %}
<ul data-category-name={{category.name}}>
{% for work in category.works %}
<li data-thumbnail-path={{thumbnail.url}} data-url={{content.url}} >
<div>
<p class="gallery1DecHeader">{{work.title}}</p>
<p class="gallery1DescP">{{work.description}}</p>
</div>
</li>
{% endfor %}
{% endfor %}
what do I need to change?
Okay, from what I can see there are a few problems. First, try adding context_object_name = 'works_list' That way you will be able to refer to the object_list as works_list like you do in the template outer for loop. The bigger problem is you are iterating over works_list.category, which according to your Work model is a Charlist(). I think you might be getting confused about what the choices kwarg does and expecting {% for category in works_list.category %} to iterate over your choices and giving you the list of cats you defined in categories. As far as I know, that's not how choices works.
If you go to your admin panel and add a new entry for your Work model, you'll see that category has a dropdown list that contains your list of cats. So, choices defines a set of legal category options for new Work objects, not a list in existing Work objects.
I think what you actually want is an additional model Category which defines: work = models.ForeignKey(Work, on_delete=models.CASCADE) as a one-to-many relationship. Basically, you want is for Work to have a subset of Category objects that you can iterate over. This will involve redesigning the way you structure and access your data though.
You need to change at least your views.py and template.html. Add a context_object_name and an extra context(Doc Link)
views.py
class WorksListView(ListView):
template_name = 'template.html'
model = Work
context_object_name = 'work_list'
def get_queryset(self):
return Work.objects.filter(published=True).order_by('-date')
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(WorksListView, self).get_context_data(**kwargs)
# Insert categories so that it can be used in template
context['categories'] = Work.categories
return context
template.html
{% for category in categories%}
<ul data-category-name={{category.0}}>
{% for work in work_list %}
{% if category.0 == work.category %}
<li data-thumbnail-path={{work.thumb_url}} data-url={{work.content_url}} >
<div>
<p class="gallery1DecHeader">{{work.title}}</p>
<p class="gallery1DescP">{{work.description}}</p>
</div>
</li>
{% endif %}
{% endfor %}
</ul>
{% endfor %}

Categories

Resources