Detect row difference (view or model)? - django

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

Related

Django nested QuerySet yielding nothing

I am using Django 1.8 with Postgres 9.2 on a Windows 8 machine.
I have two pieces of nearly identical code from two of my projects. One works and the other doesn't.
Here's the code that works:
# views.py
from django.shortcuts import render
from models import Artist, Track
def music(request):
artists = Artist.objects.all().order_by('orderName')
artistTrackCollections = []
for artist in artists:
artist.tracks = Track.objects.filter(artist=artist).order_by('order')
artistTrackCollections.append(artist)
return render(request, 'music.html', {'artistTrackCollections': artistTrackCollections,})
And the relevant template code:
{% for artist in artistTrackCollections %}
<dl>
<dt>
{% if artist.website %}
<h2>{{ artist.name }}</h2>
{% else %}
<h2>{{ artist.name }}</h2>
{% endif %}
</dt>
<dd>
<ul>
{% for track in artist.tracks %}
<li>“{{ track.title }}”
<i>({{ track.album.title }})</i>
{% endfor %}
</ul>
</dd>
</dl>
{% endfor %}
Now here's pretty much the exact same code from a different project of mine that doesn't work anymore:
def index(request):
productList = PartModel.objects.filter(isBuild=True, isActive=True).order_by('name')
productCollection = []
for product in productList:
product.part_list = product.buildpart.all().order_by('family__type')[:5]
productCollection.append(product)
return render(request, 'index.html', { 'productCollection': productCollection, })
and its corresponding template:
{% for product in productCollection %}
<p>{{ product.name }}
<p>${{ product.price }}
<ul>
{% for part in product.part_list %}
<li>{{ part.family.type.name }} | {{ part.name }}
{% endfor %}
</ul>
{% endfor %}
This code used to work but now it doesn't. In the code that works I succeed in going through each artist and attaching that artist's tracks. In the code that fails I try to go through each PartModel instance that is a build and get that PartModel instance's corresponding parts. They are identical as far as I can tell. productCollection gets populated but the part_list data for some reason is blank. This leads me to believe the problem is with this line:
product.part_list = product.buildpart.all().order_by('family__type')[:5]
But I cannot discern the difference from this line:
artist.tracks = Track.objects.filter(artist=artist).order_by('order')
Thanks in advance for any help!

how to query for the full name in django

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

How to only get first object in template

I have the following code, where I get all problem notes.
{% for n in task.task_notes.all %}
{% if n.is_problem %}
<li>{{ n }}</li>
{% endif %}
{% endfor %}
How would I only get the first problem note? Is there a way to do that in the template?
In the view:
context["problem_tasks"] = Task.objects.filter(is_problem=True)
# render template with the context
In the template:
{{ problem_tasks|first }}
first template filter reference.
Would be even better, if you dont need the other problem tasks at all (from 2nd to last):
context["first_problem_task"] = Task.objects.filter(is_problem=True)[0]
# render template with the context
Template:
{{ first_problem_task }}
Assuming you need all of the tasks in the template somewhere else.
You can make a reusable custom filter (take a look at first filter implementation btw):
#register.filter(is_safe=False)
def first_problem(value):
return next(x for x in value if x.is_problem)
Then, use it in the template this way:
{% with task.task_notes.all|first_problem as problem %}
<li>{{ problem }}</li>
{% endwith %}
Hope that helps.
use this code in the loop:
{% if forloop.counter == 1 %}{{ n }}{% endif %}

what is the right way to query a manytomany field in django

so i have a model which is,
class Category(SmartModel):
item=models.ManyToManyField(Item)
title=models.CharField(max_length=64,help_text="Title of category e.g BreakFast")
description=models.CharField(max_length=64,help_text="Describe the category e.g the items included in the category")
#show_description=check box if description should be displayed
#active=check box if category is still avialable
display_order=models.IntegerField(default=0)
def __unicode__(self):
return "%s %s %s %s " % (self.item,self.title, self.description, self.display_order)
and as you may see, it has a manytomany field
item=models.ManyToManyField(Item)
i want to return all the items in a template, here is my views.py for this
def menu(request):
categorys= Category.objects.all()
items= categorys.all().prefetch_related('item')
context={
'items':items,
'categorys':categorys
}
return render_to_response('menu.html',context,context_instance=RequestContext(request))
here is how am doing it in the templates,
<ul>
{% for item in items %}
<li>{{ item.item }}
</li>
</ul>
{% endfor %}
after all this,this is what it is returning in my web page,
<django.db.models.fields.related.ManyRelatedManager object at 0xa298b0c>
what am i doing wrong,I have really looked around but all in vain, hoping you can help me out and thanking you in advance
Exactly, you have a many to many manager. You need to actually query something... like all()
{% for item in items %}
{% for i in item.item.all %}
{{ i }}
{% endfor %}
{% endfor %}
Based on your variable naming, I think you're confusing the results of prefetch_related as a bunch of items. It is in fact returning a QuerySet of Category objects.
So it would be more intuitive to call them categories.
{% for category in categories %}
{% for item in category.item.all %}
{{ item }} {# ...etc #}
Try to use:
categorys= Category.objects.prefetch_related('item').all()
And then in template:
{% for category in categorys %}
{% for item in category.item.all %}
{{ item }}
{% endfor %}
{% endfor %}

tree structure of parent child relation in django templates

how do i implement the tree structure in django templates with out using django-mptt.
i have model.
class Person(TimeStampedModel):
name = models.CharField(max_length=32)
parent = models.ForeignKey('self', null=True, blank=True, related_name='children')
now i want ..
Parent
Child 1
subchild 1.1
subchild 1.2
nextsubchild 1.2.1
Child 2
Child 3
there names should be click able to show their profile.
I just finished implementing this. I wanted a tree structure for a sub-navigation, but I did not want to do anything strange with recursive templates.
The solution I implemented is very simple: I simply recurse in the view (in my case a generic helper function) and flatten out the hierarchical structure into a simple list. Then, in my template I just use a for loop to iterate over the list.
Each element in the list can be one of three things: "in", the object, or "out". In my case, I'm constructing a series of ul li elements in the view, so when I encounter "in" I create a new ul, when I encounter "out" I close the ul. Otherwise, I render the item.
My template code looks like this:
<ul>
{% for item in sub_nav %}
{% if item == "in" %}
<ul>
{% else %}
{% if item == "out" %}
</ul>
</li>
{% else %}
<li>
<a href='{{item.full_url}}'>{{item.name}}</a>
{% if item.leaf %}
</li>
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
</ul>
The code in the helper function looks like this:
def get_category_nav(request,categories=None):
"""Recursively build a list of product categories. The resulting list is meant to be iterated over in a view"""
if categories is None:
#get the root categories
categories = ProductCategory.objects.filter(parent=None)
categories[0].active=True
else:
yield 'in'
for category in categories:
yield category
subcats = ProductCategory.objects.select_related().filter(parent=category)
if len(subcats):
category.leaf=False
for x in get_category_nav(request,subcats):
yield x
else:
category.leaf=True
yield 'out'
Using those snippets, you should be able to build any sort of hierarchical tree you'd like without doing any recursion in the template, and keeping all the logic in the view.
I know there was already an accepted answer for this, but I thought I'd post the technique in case it helps anyone else.
from Django while loop question and
http://docs.djangoproject.com/en/dev/howto/custom-template-tags/#inclusion-tags
# view.py
#register.inclusion_tag('children.html')
def children_tag(person):
children = person.children.all()
return {'children': children}
# children.html
<ul>
{% for child in children %}
<li> {{ child }}</li>
{% if child.children.count > 0 %}
{% children_tag child %}
{% endif %}
{% endfor %}
</ul>
# your template
{% children_tag parent %}
These are great answers but I consolidated a bit and put it on the actual model.
class RecursiveThing(models.Model):
name = models.CharField(max_length=32)
parent = models.ForeignKey('self', related_name='children', blank=True, null=True)
def as_tree(self):
children = list(self.children.all())
branch = bool(children)
yield branch, self
for child in children:
for next in child.as_tree():
yield next
yield branch, None
And then in your template:
<ul>
{% for thing in things %}
{% for branch, obj in thing.as_tree %}
{% if obj %}
<li>{{ obj.name }}
{% if branch %}
<ul>
{% else %}
</li>
{% endif %}
{% else %}
{% if branch %}
</ul>
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}
</ul>
It's very simple
All you have to do in your view is get all objects:
people = Person.objects.all()
Then in your template :
{% for person in people %}
<li>- {{person.name}} </li>
{% for child in person.children.all %}
<ul>* {{child.nom}} </ul>
{% endfor %}
</li>
{% endfor %}