I'm trying to build a simple blog with Django and am trying to display blog posts and the number of comments on associated with a post. Unfortunately, I'm running into trouble getting my dictionary to print out values--or, at least where I want to.
In my views.h file:
class IndexView(generic.TemplateView):
template_name = 'blogs/index.html'
num_comments = { }
def get_blogs(self):
"""
Returns the last 5 published blog posts.
"""
blogs = BlogPost.objects.filter(
pub_date__lte = timezone.now()
).order_by('-pub_date')[:5]
for blog in blogs:
# Setting our num_comments dictionary by getting
# the number of comments from a particular blog post
self.num_comments[blog.id] = len(Comment.objects.filter(blog_post = blog.id))
return blogs
In my index.html file:
{{ view.num_comments }}
{% if view.get_blogs %}
{% for blog in view.get_blogs %}
<div>
<h1>{{ blog.post_title }}</h1>
<p>{{ blog.post_text }}</p>
<ul>
{{ blog.id }}
{{ view.num_comments }}
{% for key, value in view.num_comments %}
<li>
{{ key }} <-- Does not display
{{ value }} <-- Does not display
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
{% else %}
<p>No blogs are available.</p>
{% endif %}
Where I explicitly call {{ view.num_comments }}, the correct dictionary is being displayed. Any idea why my dictionary isn't getting the key and value pair correctly? Thanks.
You should use .items() method to access the key and value pairs:
{% for key, value in view.num_comments.items %}
Also see the third example in Django documentation.
Related
I don't really know how to phrase my question, but what I'm trying to do is add a title between items to split them into obvious categories on my template. I would like to do this:
{# set variable "current_category" to an empty value first #}
{% for item in items %}
{% if item.category != current_category %}
{{ current_category = item.category }} {# <-- How to do that? #}
<h1>{{ item.category }}</h1>
{% endif %}
<p>{{ item.name }}</p>
{% endfor %}
Then end up with:
<h1>Cat 1</h1>
<p>item</p>
<p>item</p>
...
<h1>Cat 2</h1>
<p>item</p>
..
I saw on similar answers that there are things called custom filters and simple tags but it seems really complicated for something very simple. Is this really the only way to do that?
NOTE: I already ordered the items by category of course
I think you're looking for Django's regroup template tag, which does exactly what you're asking for.
{% regroup items by category as grouped_items %}
{% for category, item_list in grouped_items %}
<h1>{{ category }}</h1>
{% for item in item_list %}
<p>{{ item.name }}</p>
{% endfor %}
{% endfor %}
Is there a way to say to Django to hide/remove (show a blank space) for fields that got same values as previous row?
i.e.: if now is equal for differents Articles can it be show only for the first of the group?
from django.views.generic.list import ListView
from django.utils import timezone
from articles.models import Article
class ArticleListView(ListView):
model = Article
def get_context_data(self, **kwargs):
context = super(ArticleListView, self).get_context_data(**kwargs)
context['now'] = timezone.now()
return context
<h1>Articles</h1>
<ul>
{% for article in object_list %}
<li>{{ article.pub_date|date }} - {{ article.headline }}</li>
{% empty %}
<li>No articles yet.</li>
{% endfor %}
</ul>
Article - now
a - 2017-01-01
b -
c - 2017-01-02
d -
Is this possible from view or directly in template?
You can use ifchanged which:
Checks if a value has changed from the last iteration of a loop.
as follows:
<h1>Articles</h1>
<ul>
{% for article in object_list %}
<li>{{ article.headline }} - {% ifchanged article.pub_date|date %}
{{ article.pub_date|date }} {% endifchanged %}
</li>
{% empty %}
<li>No articles yet.</li>
{% endfor %}
</ul>
This will check in each iteration the value of article.pub_date and only when that value changes it will be displayed.
Good luck :)
It is able to write {{ myval.add:5 }}, {{ myval|add:value }} and even {{ myval|add:-5 }}.
However, I can't find out what I should type to add value * -1 like {{ myval|add:-value }}. This doesn't work, sadly.
You need to use double quotes:
{{ myval|add:"-5" }}
This subtracts five from myval.
The built-in Django template tags/filters aren't all-encompassing, but it's super easy to write your own custom template tags: https://docs.djangoproject.com/en/dev/howto/custom-template-tags/
You could make your own subtract template tag pretty easily:
#register.filter
def subtract(value, arg):
return value - arg
Use django-mathfilters from PyPI: https://pypi.python.org/pypi/django-mathfilters
To install :
$ pip install django-mathfilters
Then add mathfilters in your INSTALLED_APPS.
In template:
{% load mathfilters %}
<ul>
<li>8 + 3 = {{ 8|add:3 }}</li>
<li>13 - 17 = {{ 13|sub:17 }}</li>
{% with answer=42 %}
<li>42 * 0.5 = {{ answer|mul:0.5 }}</li>
{% endwith %}
{% with numerator=12 denominator=3 %}
<li>12 / 3 = {{ numerator|div:denominator }}</li>
{% endwith %}
<li>|-13| = {{ -13|abs }}</li>
</ul>
I recently started working with Django and stumbled upon this one as well: I needed a very simple template loop that stops printing after n times and shows a "more" link to toggle the rest of the items.
With great interest I read the struggle of people trying to understand why this is not being added to the Django default filters (since before 2013). I didn't feel like creating a custom template tag and I kind of found a way to subtract 2 variables using strings and add in combination with with and stringformat
Let's say I have a list of items where I want to print the first 2 and hide the rest, showing how many hidden items are there, eg.
John, Anna and 5 others like this (when given a list of 7 items)
As long as the number of visible items is harcoded in the template (eg. 2), it's possible to add the negative 2 |add:"-2", but I wanted the number of visible items to be a variable as well. The Math-filter library as suggested above doesn't seem up to date (I haven't tested it with Django 2.x).
The trick seems to be to use the add helper to concat the strings "-" with the integer as string, so it can be coerced back to a negative integer in a any consecutive calls to the add helper. This doesn't work however if the value is not a string, so that's where the stringformat helper comes in.
With string value
template posts.html (note how visible is explicitely passed as string - alternative below)
{% for post in posts %}
<h4>{{ post.title }}</h4>
...
{% include 'show_likes.html' with likes=post.likes visible="3" %}
{% endfor %}
template show_likes.html (note the add:0 to make the boolean operator work)
{% with show=visible|default:"2" %}
{% for like in likes %}
{% if forloop.counter <= show|add:0 %}
{% if not forloop.first %},{% endif %}
{{ like.username }}
{% endif %}
{% endfor %}
{% if likes|length > show|add:0 %}
{% with rest="-"|add:show %}
and {{ likes|length|add:rest }} more
{% endwith %}
{% endif %}
like this
{% endwith %}
Alternative with integer
You could just convert your integer to a string in the calling template using |stringformat:"d"
If however the number of visible items you want to show is an integer, you'll have to add a call to stringformat:"d" to have it converted to string
template posts.html
{% for post in posts %}
<h4>{{ post.title }}</h4>
...
{% include 'show_likes.html' with likes=post.likes visible=3 %}
{% endfor %}
template show_likes.html
{% with show=visible|default:2 %}
{% with show_str=show|stringformat:"d" %}
{% for like in likes %}
{% if forloop.counter <= show %}
{% if not forloop.first %},{% endif %}
{{ like.username }}
{% endif %}
{% endfor %}
{% if likes|length > show|add:0 %}
{% with rest="-"|add:show_str %}
and {{ likes|length|add:rest }} more
{% endwith %}
{% endif %}
{% endwith %}
{% endwith %}
Since I'm a very beginner with Django and Python, I'm pretty sure this approach is far worse than actually creating a custom helper! So I'm not suggesting anyone should be using this. This was just my attempt on trying to solve this with the available template helpers and without any custom stuff.
Hope this helps
Lo primero es multiplicar por -1 para convertirlo en una valor negativo y guardarlo en una variable y posterior a usar la suma
The first thing is to multiply by -1 to turn it into a negative value
and save it in a variable and then use the add
{% widthratio val2 1 -1 as result %}
{{result|add:val1}}
After search I found that I can make {% with var=value %} with filters to make the arithmetic operations "with other variables or not"
For example: I have x = 5 and y = 3 and need to add the y's value to x value, all what I need is these steps:
1- Create variable x : {% with x=5 %}
2- Create variable y : {% with y=3 %}
3- In my HTML tags, say <h1>, write that : <h1>{{ x|add:y }}</h1>
4- Close the y's with : {% endwith %}
5- Close the x's with : {% endwith %}
Hope it works with you, it worked with me.
{% with i=3 %}
{% with x=1 %}
<h1>{{i|add:x}}</h1> <!-- result is 4 -->
{% endwith %}
{% endwith %}
I want to print value by id in database,And don't know which keywords to find in Google.
in my views.py, I send transen = TransEn.objects.all() to template
and this will print all datas from database:
{% for words in transen %}
{{words.words|safe }}
{% endfor %}
But I want to print by the value of the id Like:
(Because they are words in English for translating website)
I don't know how to write this in template, please guide me, Thank you very much.
<div><span> TransEn.objects.filter(id='2') </span></div>
<div> TransEn.objects.filter(id='3') </div>
UPDATE:
I have found a method:
I can use if tag, but are there another ideas??
<div>
{% for words in transen %}
{% if words.id == 2 %}
{{ words.words|safe }}
{% endif %}
{% endfor %}
</div>
<div>
{% for words in transen %}
{% if words.id == 3 %}
{{ words.words|safe }}
{% endif %}
{% endfor %}
</div>
If you want to access each item in the QuerySet individually, by index, you should cast it to a list first. You should change your views.py to:
transen = list(TransEn.objects.all())
And then in your template you can access them by index like so:
<div><span> {{ transen.1.words }} </span></div>
<div> {{ transen.2.words }} </div>
A warning from the Django docuemtnation about casting a QuerySet to a list:
Be warned, though, that this could have a large memory overhead, because Django will load each element of the list into memory. In contrast, iterating over a QuerySet will take advantage of your database to load data and instantiate objects only as you need them.
I have successfully implemented the regroup call in my template to order missed donations by a pickup_id. The goal was to display all the routes where a a missed donation occured, and a list of all the names underneath the route name. Pickup routes can have the same name so I was grouping by pickup_id. When I do that and call {{ route.grouper }} it returns the ID of the pickup of course. How can I call the field 'route' which displays the route name from grouper?
I was trying things like this...
{{ route.grouper.route }}
{{ route.route.grouper }}
view
missed_routes = Donor.objects.filter(missed='YES').order_by('pickup_id')
template
{% regroup missed_routes by pickup_id as missed_pickups %}
{% for route in missed_pickups %}
<p>{{ route.grouper }}</p>
<ul>
{% for donor in route.list %}
<li>{{ donor.last_name }}</li>
{% endfor %}
</ul>
{% endfor %}
Grouper is just a string, so you have to get name from route instance. Not sure it's gonna work, but try {{ route.list.0.pickup.name }} (I assume that pickup is foreign key to Pickup model with name field) instead of {{ route.grouper }}