Django display table in templates - python

I am using Django 2.0.1, and I have the following code:
Models.py:
class Category(models.Model):
category_name = models.CharField(max_length=50)
class CategoryItems(models.Model):
category_name = = models.ForeignKey(Categories, related_name='categoriesfk', on_delete=models.PROTECT)
item_name = models.CharField(max_length=50)
item_description = models.CharField(max_length=100)
Thereafter my views.py:
def data(request):
categories_query = Categories.objects.all()
category_items_query = CategoriesItems.objects.all()
return render_to_response("data.html",
{'categories_query': categories_query,'category_items_query': category_items_query}
In the template I'm trying to display all items for each category, for example, suppose there are 4 categorizes, e.g. Bicycle, then it display all items belonging to that category only. For example, as follows:
Category 1:
Category_Item 1,
Category_Item 2,
Category_Item 3,
and so on ...
Category 2:
Category_Item 1,
Category_Item 2,
Category_Item 3,
and so on ...
I have tried to write so many different for-loops, but they just display all items, I need it to show items only for that category, then for loop to next category and show items for that.

You don't need your category_items_query variable, just category_query:
{% for category in category_query %}
<b>{{ category.category_name }}</b><br />
{% for item in category.categoriesfk.all %}
{{ item.item_name }}<br />
{% endfor %}
{% endfor %}
Your related_name of categoriesfk is weird, it'd make more sense to be something like items.

What you need is two for loops with an if to check if the second loop should belong to the first.
Try this:
{% for category in categories_query %}
{% for category_item in category_items_query %}
{% if category_item.category_name == category %}
{# do something with category_item #}
{% endif %}
{% endfor %}
{% endfor %}
I believe it would be more clear if you named the ForeignKey in CategoryItems to just "category", instead of "category_name", since this field will have the category itself, not just it's name. Your "if" would then be more readable and make more sense:
{% if category_item.category == category %}
Hope it helps.

A few things,
Since your model name is already Category, your field names should be like name instead of category_name.
Model names must be singular, so it should beCategoryItem instead of CategoryItems
When you do a model_name.objects.all(), you do not get a query but a Queryset, make your variable names such that they describe what they do. Currently, categories_query is wrong. You could instead use category_qs.
Now, coming to your question, you require two for loops. One to loop through the categories and then one to loop through items in a particular category.
Something like,
for category in category_qs:
for item in category:
# Do something with item
You have the basic idea here, now you can convert it to real working code. Good luck!

Grouping data in template is not best idea as template should be logic free,
also good idea would be to use database capabilities and use select_related to query out related set
category_data = Category.objects.select_related('item').all()
afterwards you could do following in template
{% for category in category_data %}
{# print category #}
{% for item in category.items %}
{# print item #}
{% endfor %}
{% endfor %}

Django has a built-in regroup template tag for this functionality.
{% regroup category_items_query by category_name as categoryname_list %}
{% for category in categoryname_list %}
<strong>{{ category.grouper }}</strong><br>
{% for item in category.list %}
{{ item.item_name }}: {{ item.item_description }}<br>
{% endfor %}
{% endfor %}
You have to order your CategoryItems for this functionality to work.
category_items_query = CategoriesItems.objects.all().order_by('category_name')

Related

Access a view variable within a template

I have a context variable which is dynamic because of a loop I perform:
context['categories'] = choices.CATEGORIES
for category_type, category in context['categories']:
context[category_type] = Article.objects.filter(category=category_type).count()
But in the template it just prints the category type instead of the number which is on the variable but I also need type for a link:
{% for type, category in categories %}
{{category}}
{{type}}
{% endfor %}
How can I access this dynamic variable in a for loop?
You should use the items method in for loop like:
{% for type, category in categories.items %}
{{ category }}
{% endfor %}

Django tell if last item with certain attribute in dual ordering (order_by)

In a Django queryset how can I tell if the list item I'm on (when iterating) is the list item that has a certain attribute.
Example: I order a queryset by:
MyModel.objects.all().order_by('-featured', '-created_at')
So, all objects which are featured first by their appropriate time and then all non-featured items sorted by time properly as well.
When iterating in the template I want to be able to tell if I've hit the last "featured" item in the dual-ordering (such that featured = True).
Example usage: If last item -> display banner separating featured / non-featured -> continue iterating displaying non-featured after banner.
{% for object in object_list %}
# display object
{% if last item with attribute %}
# display banner
{% endif %}
{% endfor %}
I know I can do another queryset to get the count, but that's an extra query I'd like to avoid.
You can group the items in the queryset using itertools.groupby before passing it to the template. Then you'll show the banner when the group key changes from True to False:
from itertools import groupby
qs = MyModel.objects.all().order_by('-featured', '-created_at')
grouped_qs = groupby(qs, lambda x: x.featured)
And then in your template:
{% for k, g in grouped_qs %}
{% if not k %}
<!- show banner -->
{% endif %}
{% for model_obj in g %}
<!- do something with model_obj -->
{% endfor %}
{% endfor %}

Django Template Foreign Key Check

In model:
class Example
text
class Share
example (foreign_key)
user (foreign_key)
In view:
def page:
items = Example.objects.get(user=user)
user_shares = Example.objects.get(user=user)
return render(page.html, {'items': items, 'user_shares': user_shares})
In the template I can show items in rows. But for the shared ones I want to put for example additional buttons.
How can I use something like {% if item in shares %} in for loop? Or do you have better ideas?
in the template:
{% for item in items %}
<td>{{item.text}}</td>
{%if item.shares.count > 0 %}
<td><!-- additional buttons here --></td>
{% endif %}
{% endfor %}
you need to modify the ForeignKey in the Example model:
class Share(models.Model):
example = models.ForeignKey(Example, related_name='shares')
...
Hope this helps.

querying foreignkey nested for loop django

I am trying to return a list of categories for a business, and for each category I would like to list all the items related to the category.
I was returning all of my items, not by category, but I have decided I want them sorted by category. This is what I have tried (among other attempts as well) I simply am having trouble getting the items into there categories. This is my latest attempt
In my models.py I have
Business(models.Model):
name = models.CharField(max_length=100)
address = models.CharField(max_length=100)
logo = models.CharField(max_length=300)
ItemCategory(models.Model):
name = models.CharField(max_length=50)
Item(models.Model):
name = models.CharField(max_length=100)
business = models.ForeignKey(Business)
category = models.ForeignKey(ItemCategory)
short_description = models.CharField(max_length=250)
in my views.py
def business_profile(request, business_id):
items = Item.objects.filter(business_id = business_id).select_related('itemcategory')
return render(request, 'business_profile.html', {"items": items})
in my template I am trying
{% for itemcategory in items.itemcategory_set.all %}
{{ itemcategory.name }}
{% for item in itemcategory %}
{{ item.name }} <br>
{{ item.short_description }}
{% endfor %}
{% endfor %}
From my research into other questions and reading the documents, i felt this would be right.. However from my results I can not get the correct output in my template.
Would this be the correct practice? Or should I try getting the categories first with
categories = ItemCategory.objects.filter(business = business_id)
and then possibly add a select_related('item') to process all the items for the category?
When I refer to data in the template but my model has 2 words (ItemCategory) - for instance when I move backward for a foreign key or use the model in select_related('') - would I use item_category or do you simply use itemcategory with no underscore?
UPDATE: I answered below in comment with explanation.
To list the items by category, I used #yusuf.oguntola's idea and initially got the business with .get(), then I created a queryset to get all the items. My function in views.py included
business = Business.objects.get(id = business_id)
items = business.item_set.all().order_by('category')
note: the business_id was passed in url pattern
However, the key change was in my template. I found this awesome built in functionality for templates in the django documents here
I implemented it in template like so...
{% regroup items by category as category_list %}
<ul>
{% for category in category_list %}
<li>{{ category.grouper }}
<ul>
{% for item in category.list %}
<li>{{ item.name }}<br> {{ item.short_description }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
One of the most important parts is you have to have order_by() and put the parameter of the field you are ordering by - in my case, category. If this is not included, it will simply put the items listed in chronological order and will repeat your field instance.
items = Item.objects.filter(business_id = business_id) is wrong.
You should rather say:
items = Item.objects.filter(business__id = business_id) #Notice the double underscore.
You may also rather get the business and simply say:
business.item_set.all() #This would retrieve all Item for the business.
select_related() expects a fieldname as a paramter. So use a field name that exists on the Item model. Like:
select_related('category')
Where's the meal? May be you can proceed from there though.

Include tag in Django template language -- what can I pass in to it?

OK, so again there is likely a "simple" solution to this, but I am a beginner and nothing seems simple to me.
I have a view and a template that shows the attributes of an instance of a Car class that I have modeled. This Car class has a ManyToMany relationship with my custom User class. The template that show the attributes of a given instance of Car has many variables. The view for each Car works fine. Here is what I can't get to work:
I have a user profile page for each instance of User. From that page, I want to show the attributes of each Car that a particular User has "favorited." I am unable to figure out how to do this.
I have tried the {% include %} tag to include a snippet of the Car template and then use a for statement to iterate through the favorite set of the User. In theory, this would populate the User page with each Car that they have "favorited" and show its attributes. However, I do not know how to pass the {% include %} tag the proper context so the attributes are populated correctly for each instance of Car. Is this possible?
Is there a simpler way to do it that I am just overlooking?
Any help is appreciated. Thanks!
Use the {% include ... with ... %} syntax:
{% for car in user.favorite_cars.all %}
{% include "car.html" with name=car.name year=car.year %}
{% endfor %}
Another alternative is the {% with %} tag:
{% for car in user.favorite_cars.all %}
{% with name=car.name year=car.year %}
{% with color=car.color %}
{% include "car.html" %}
{% endwith %}
{% endwith %}
{% endfor %}
UPDATE: If data for the template can't be obtained from the Car model then you have to use the custom inclusion tag:
from django import template
register = template.Library()
#register.inclusion_tag('car.html')
def show_car(car):
history = get_history_for_car(car)
return {'name': car.name, 'history': history}
And the in the template:
{% load my_car_tags %}
{% for car in user.favorite_cars.all %}
{% show_car car %}
{% endfor %}

Categories