Access a view variable within a template - python

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 %}

Related

Django display table in templates

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')

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 %}

grouping fields in django form

I want to render form grouping fields. Form actually is created dynamically according to incoming dictionary
for f in settings.FORM_BIG_FIELDS:
self.fields[f['id']] = eval(f['type'])(label=f['label'], required=f.get('required', True))
self.fields[f['id']].groupp = f.get('group', 1)
groupp attribute means appropriate group, then I try to render it like
{% regroup form.fields.values by groupp as field_group %}
{% for group in field_group %}
<div class="group_{{ group.grouper }}">
{% for field in group.list %}
<p>
{{ field.all }}
{{ field }}
</p>
{% endfor %}
</div>
{% endfor %}
But as output I get the following
<django.forms.fields.CharField object at 0xb527388c>
<django.forms.fields.IntegerField object at 0xb52738ec>
<django.forms.fields.ChoiceField object at 0xb527394c>
I have read that these are not the same as BoundField object. How to render fields or is there any other better approaches to group fields?
If you do not want use any additional libraries, then the most easy solution is to render them manually, i would say. Otherwise you will just spend alot of time repeating the functionality of the library i copied as comment to your post.
There is always the case that things should be DRY. But we build websites for the users and user cares little about how the form rendering in template is done. For this reason we have often created form templates manually like this:
<div class="something">
{{ form.fieldname.label_tag }}{{ form.fieldname }}
</div>
Easyest way to organise it saving you some time. And in my opinion it is not that bad either, since this is not very common when you need fields organised by fieldsets.
I know this question is rather old, but I am sure there are still people who can benefit from a simple solution:
Say you have a group name and list of members. You can define a self.fieldset in your form's init to be a dictionary of {'group_1': ['member_1', 'member_2', ... ], ... }. Once you attach this to the form, you can pass it to views and from there to the template:
In forms.py:
class MyForm:
def __init__(self, current_user, *args, **kwargs):
super(YourForm, self).__init__(*args, **kwargs)
self.field['group'].queryset = Group.objects.filter(user = current_user)
...
In views.py:
form = self.Form(current_user)
the_fieldsets = form.fieldset
c = {'form': search_form,
'fieldsets': the_fieldsets }
In your template:
{% for field in form %}
<tr>
<td>{{field.label_tag}}</td>
{% if field.name == 'group' %}
<td>
<select id='{{field.id}}' name='{{field.name}}'>
{% for k,v in fieldsets.items %}
<optgroup label = {{k.name}}>
{% for val in v %}
<option name='{{val}} value = {{val.id}}> {{val.name}} </option> # Note that the select needs to return 'id', so value has to be {{val.id}}
{% endfor %}
</optgroup>
{% endfor %}
</select>
</td>
{% else %}
<td>{{field}}</td>
{% endif %}
<td>{{field.help_text}}</td>
<td>{{field.errors}}</td>
</tr>
{% endfor %}

Django template values

I am retrieving model object to my template and I am getting it like this:
value: [{'name_value9': 48}]
When filter the object in the view I am using '.values' like this:
...
name_value.values('name_value' + str(fn))
...
The reason for that is that I have 'name_value1, name_value2, etc... and I need to get to the correct one.
How can I show in the template only the result (value) without the key (name_value9)?
Thanks.
You can print values in your template like this:
{% obj in obj_list %}
{% for key, value in obj.items %}
{{ value }}
{% endfor %}
{% endfor %}

Inlines Python/Django technique for objects

I am reading the source code of the Django application blog at git://github.com/nathanborror/django-basic-apps.git.
How do you read the following Django code?
{% tags_for_object object as tag_list %}
My attempt: Make the variable object of the type tags_for_object and rename the variable to tag_list.
The object apparently is based on the file blog/templates/inlines/default.html:
{% if object %}
{{ object }}
{% else %}
{% for object in object_list %}
{{ object }}
{% endfor %}
{% endif %}
What is the befefit of putting the logic to two-step procedure: run single object, else loop through a list of objects?
It looks like tags_for_object is the template tag from the django-tagging application.
From the django-tagging documentation:
tags_for_object:
Retrieves a list of Tag objects
associated with an object and stores
them in a context variable.
Usage:
{% tags_for_object [object] as [varname] %}
Example:
{% tags_for_object foo_object as tag_list %}
You can then loop through the tag_list variable in the template to display the tags.
{% tags_for_object foo_object as tag_list %}
<ul>
{% for tag in tag_list %}
<li>{{ tag }}</li>
{% endfor %}
</ul>
For the second part of your question, you understand the code correctly. If the variable object exists in the context (and doesn't evaluate to False), it is displayed. If it does not exist in the context (or if it evaluates to False), then the code loops through the objects in object_list, and displays them.
As for why you would want to do this, you would have to look at the code that uses inlines/default.html to work out what the designer had in mind.

Categories