I'm using Django 1.5.8
I'd like to filter Datefield type data in template like following code.
express with timesince format for recent articles
express with date format for old articles
some_template.html
{% for article in articles %}
{# recent articles #}
{% if article.created >= (now - 7 days) %}
{{ article.created|timesince }}
{# old articles more than one week past #}
{% else %}
{{ article.created|date:"m d" }}
{% endif %}
{% endfor %}
Is there a solution to handle {% if article.created >= (now - 7 days) %} by django's own template tags?
Or do I have to make new custom filter?
Although I'm sure it's possible to do this with a custom template tag, I think you'll find it is a lot easier to implement this test in your model code. For example:
from datetime import date, timedelta
class Article(models.Model):
[...]
def is_recent(self):
return self.created >= date.today() - timedelta(days=7)
Then your template can be:
{% for article in articles %}
{% if article.is_recent %}
{{ article.created|timesince }}
{% else %}
{{ article.created|date:"m d" }}
{% endif %}
{% endfor %}
Related
In order to make sure my database values don't create null value exceptions I created a dummy date of 01-01-0001 in Postgresql.
However I want my views to return none if it's the dummy date in the view.
I tried the following but it's not working:
{% if myDate == "0001-01-01" %}
None
{% else %}
{{ myDate }}
{% endif %}
You can use Django date filter template tag
{% if myDate|date:"Y-m-d" == "0001-01-01" %}
None
{% else %}
{{ myDate }}
{% endif %}
I have a template which will display all the likes and the person liked for a particular forum. In the template it can display numbers of likes and all the person's username that liked that forum. But I want the full name and not the username (here it is the email). How do I get the full name in the template or if possible from the view itself. Thank you.
forums.html:
{% extends "base.html" %}
{% load forum_tags %}
{% block content %}
<h2>Logged in as -- {{request.user}}</h2>
<h1>Forums:</h1>
{% if forums.count > 0 %}
{% for forum in forums %}
<h2>{{forum.question}}</h2>
<p>{{forum.body | truncatewords:"30"}}</p>
{% if user in forum.likes.all and forum.likes.count > 1 %}
<p>Unlike You and {{forum.likes.count | substract:1}} others liked</p>
{% elif user in forum.likes.all %}
<p>You liked it</p>
{% else %}
<p>Like</p>
{% endif %}
{% for likes in forum.likes.all %}
<li>{{likes}}</li>
{% endfor %}
{% endfor %}
{% else %}
<p>Sorry! No forum to display.</p>
{% endif %}
{% endblock %}
snippet of views.py:
def forums(request):
forums = Forum.objects.all()
c = {'forums': forums}
return render(request, 'forums.html', c)
If you're using the default User model from django.contrib.auth.models, it has a get_full_name method that you can use in your template:
{{ user.get_full_name }}
Otherwise, you can implement that method in your own User model too. Any method that accepts no arguments can be called from templates (unless they have a alters_data attribute set to True).
I would like to display a list of publications on my website; however, I would also like to diaplay a header stating the year for each set of publications published on that particular year.
So I would like for my end result to be like this (my reputation is 1 :( I could not upload the image):
https://dl.dropboxusercontent.com/u/10752936/Screen%20Shot%202013-06-21%20at%206.00.15%20PM.png
I have a table with three columns; id (primary key), title (the title of the article), and date (the date of publications)
In my template file; doing the following will print the header before every article:
{% for curr_pub in all_publications %}
<h1>{{ curr_pub.date.year }}</h1>
<li>{{ curr_pub.title }}</li>
{% endfor %}
I am passing all_publications ordered by '-date' which means that I can compare the year of the current row curr_pub with the previous one and check if it differs or not; and print (or not print) the header accordingly. It seems however, that I cannot do that in the template.
Since I am new to Django and Python, I wasn't sure what to do and this is where I need help; my thoughts were the following:
1) Add a function in the model (def is_it_first_publication(self):) that returns true or false - but I really wasn't able to do that :| - ...and I'm not sure if that is what I needed to do or not!
2) Second one is to do in in the view, and pass extra variable(s) to the template; here's an example (which works just fine for this case):
In the view:
def publications(request):
all_publications = Publications.objects.order_by('-date')
after_first_row_flag = False
f_year = 'Null'
list_of_ids_of_first_publications = []
for curr_pub in all_publications:
if after_first_row_flag:
if curr_pub.date.year != f_year:
list_of_ids_of_first_publications.append(curr_pub.id)
f_year = curr_pub.date.year
else:
# The year of first (or earliest) publication has to be added
#
list_of_ids_of_first_publications.append(curr_pub.id)
f_year = curr_pub.date.year
after_first_row_flag = True
template = loader.get_template('counters/publications.html')
context = RequestContext(request, {
'all_publications': all_publications,
'list_of_first_publications': list_of_ids_of_first_publications,
})
return HttpResponse(template.render(context))
In the template:
{% for curr_pub in all_publications %}
{% if curr_pub.id in list_of_first_publications %}
<h1> {{ curr_pub.date.year }} </h1>
{% endif %}
<li> Placeholder for [curr_pub.title] </li>
{% endfor %}
The regroup built in filter can do this for you without annotating your objects in the view. As the documentation says, it's kind of complicated.
https://docs.djangoproject.com/en/dev/ref/templates/builtins/#regroup
{% regroup all_publications by date.year as year_list %}
{% for year in year_list %}
<h1>{{ year.grouper }}</h1>
{% for publication in year.list %}
<li>{{ publication.title }}</li>
{% endfor %}
{% endfor %}
I think you want the regroup template tag;
{% regroup all_publications by date as publication_groups %}
<ul>
{% for publication_group in publication_groups %}
<li>{{ publication_group.grouper }}
<ul>
{% for publication in publication_group.list %}
<li>{{ publication.title }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
Maybe the template tag regroup could help.
Alternatively, you could do this grouping by year in the view function (will try to provide code later).
I noticed that my django code calls my database very often with the exact same queries.
I understand that a db hit is made when I actually need the data to display on a page or to evaluate. However, my template code looks like this:
template:
{% if item.listing %}
{{ item.name }} text <strong>{{ item.listing|lowestprice }}</strong> more text
{% else %}
{{ item.name }} even more text
{% endif %}
....
{% for listed_item in item.listing %}
....
{% endfor %}
custom filter:
def lowestprice(value):
try:
val = unicode(value[0].price) + unicode(value[0].symbol)
return val
except:
return "not available"
This code hits my db three times. First on template {% if .. %} second on my custom filter, third on the {% for %} loop.
listing is a method of my models class which is returning a raw SQL queryset with some very expensive joins.
def listing(self):
return Universe.objects.raw("ONE HELL OF A QUERY")
How can I reduce my code to hit the db only once?
Edit: Using with works, but is it possible to avoid db hits on custom filters?
You should use with to do the expensive query once and store it the context.
{% with item.listing as item_listing %}
{% if item_listing %} ... {% endif %} ... etc ...
{% endwith %}
Given is a model called "comment" with a foreign key relationship to a model called "task".
{% for task in tasks %}
{% for comment in task.comment_set.all %}
{{ comment }}
{% endfor %}
...
What is the best way to limit this to 5 comments like:
Entry.objects.all()[:5]
{% for task in tasks %}
{% for comment in task.comment_set.all|slice:"5" %}
{{ comment }}
{% endfor %}
{% endfor %}
You don't. You should not do "real work" in a template, this breaks the MVC pattern.
Do the real work in the view, and pass the data to the template (using the context dictionary).
def handle_comments(request):
tasks = Task.objects.all()
comments = {}
for task in tasks:
comments[task] = task.comment_set.all()[:5]
return render_to_response('commenting.html', {'comments': comments})
You can then iterate over the comments in your template:
{% for task, task_comments in comments.items %}{{ task }}{% endfor %}