I have a model Comments with a foreign key to another model BaseRecipe. I have a DetailView calling the BaseRecipe. I am doing the following to display all the comments for BaseRecipe:
{% for comment in object.comment_link.all %}
<h5>Comment {{ forloop.counter }}</h5>
<p>{{ comment.comment_text }}</p>
<p>{{ comment.pub_date |date }}</p>
{%endfor %}
This works great.But, I want to change this so that it only shows the last x number of comments. I have tried:
{% for comment in object.comment_link.all{[:5] %}
&
{% for comment[:5] in object.comment_link.all %}
I tried some different logic, but it would not let me set initial values for loops (not sure if you can do that in templates). Im thinking maybe i should create a variable in the view that is a list of the last x numbers of comments? It seems like there should be some easy operator to do this that i have not stumbled across.
You can use slice
{% for comment in object.comment_link.all|slice:":10" %}
I don't think this is possible in templates. As you do not only want 5 comments but you also want to order them (latest 5). You can add a method in your BaseRecipe model class to get recent comments.
class BaseRecipe(models.Model):
def get_recent_comments(self, n=5):
return self.comment_link.all().order_by('-id')[:n]
Then call this method in your template
{% for comment in object.get_recent_comments %}
Related
I have a HTML template in django. It get's two variables: list of categories (queryset, as it it returned by .objects.all() function on model in django) and dictionary of contestants. As a key of the dictionary, I'm using id of category, and value is list of contestats.
I want to print name of the category and then all the contestants. Now I have this:
{% for category in categories_list %}
<h1>category.category_name</h1>
{% for contestant in contestants_dict[category.id] %}
{{ contestant }} </br>
{% endfor %}
{% endfor %}
However, when I run it, I get error:
TemplateSyntaxError at /olympiada/contestants/
Could not parse the remainder: '[category.id]' from 'contestants_dict[category.id]'
What I know so far is that I can't use index in template. I thought that {% something %} contains pure Python, but it shoved up it's just a tag. I know that I have to create my own simple_tag, but I don't know how. I read the docs Writing custom template tags, but there is such a little information and I wasn't able to fiqure out how to create (and mainly use in a for loop) a tag, that will take dict, key and return the value. What I tried is:
templatetags/custom_tags.py:
from django import template
register = template.Library()
#register.simple_tag
def list_index(a, b):
return a[b]
and in template:
{% for contestant in list_index contestants_dict category.id %}
But I get TemplateSyntaxError.
Could you please explain/show me how to create the tag, or is there a better way to do this?
Thanks.
//EDIT:
I managed to do it this way:
{% list_index contestants_list category.id as cont %}
{% for contestant in cont %}
it works, but it takes 2 lines and I need to create another variable. Is there any way to do it without it?
If you don't want 2 lines like that you should be able to use a filter i think
#register.filter
def list_index(a, b):
return a[b]
Then the usage like this
{% for contestant in contestants_dict|list_index:category.id %}
{{ contestant }} </br>
{% endfor %}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Get list item dynamically in django templates
How to get list items with variable counter. The aim is to get the so item instead of list
not like this
{% for item in listModels %}
{{ item | safe }}
{% endfor %}
so work
{% for i in listModels|length|get_range %} // get size list
{{ listModels.i | safe }}
{% endfor %}
do not work
{% for i in listModels|length|get_range %} // get size list
{{ listModels.i | safe }}
{% endfor %}
Django templates will not allow you to do this. I'm not going to lecture you on keeping your logic out of your templates, because I think it's a stylistic choice. But understand that this is the easiest way. If you need to use the index, you can access it as a forloop property, as explained in the documentation.
If you really want variable indexing, you could make your own custom template tag to do it. But, in this case, I suggest you use a more powerful templating language, like Jinja2, instead of torturing the Django templating language.
Django Template Language provides you a way to do this...
{% for item in listModels %}
{{ forloop.counter }}
{% endfor %}
I fixed like I did the following:
{{ listModelsData|lookup:i|lookAttribute:"author" }} -// this equal listModelsData[i].author - this code in python
#register.filter
def lookAttribute (d, token):
return getattr (d, token)
Hi I am using App Engine/Python to do a simple website. I have some trouble with a Django template problem.
In short, I want to use a "ShortName" to access a "LongName".
The soource code:
LongName={"so":"stackoverflow","su":"superuser"}
ShortName=['so','su']
Then I pass these two parameters to the templates.
In the template I write:
{% for aname in ShortName %}
{{ aname }} stands for {{ LongName.aname }},
{% endfor %}
The output is:
so stands for, su stands for
No error is given. The LongName.aname wont work.
I have no idea whats wrong.
This is trying to access LongName['aname'], not LongName[aname].
You might have to write a custom template tag/filter to get this to work. This Django bug (marked WONTFIX) has a simple implementation:
def get(d, key):
return d.get(key, '')
register.filter(get)
which you would use by
{{ LongName|get:aname }}
after adding it to your app (that SO answer shows how to do it on GAE).
You could also pre-make a variable to loop over in the view, by passing in
# in view
name_abbrevs = [(k, LongName[k]) for k in ShortName]
# in template
{% for short_name, long_name in name_abbrevs %}
{{ short_name }} stands for {{ long_name }}
{% endif %}
If you really don't want to add a template tag -- which isn't that bad! you just make one file! :) -- or pass in an extra variable, Vic's approach will let you do this without touching the Python files at all. As he mentions, it involves a lot of pointless iteration, but it'll work fine for small lists.
Django templates have a drawback here. I've been in the same situation before. What you have to do is iterate over all the keys in LongName, and check if the key you're looking for matches the ShortName. Here you go:
{% for aname in ShortName %}
{% for short_version, long_version in LongName %}
{% if aname == short_version %}
{{ aname }} stands for {{ long_version }},
{% endif %}
{% endfor %}
{% endfor%}
It's inefficient, and essentially a pointless O(n^2) mechanism. However, there's no better way in pure Django templates to refer to entries of a dict by a variable name.
Is it possible to sort a set of related items in a DJango template?
That is: this code (with HTML tags omitted for clarity):
{% for event in eventsCollection %}
{{ event.location }}
{% for attendee in event.attendee_set.all %}
{{ attendee.first_name }} {{ attendee.last_name }}
{% endfor %}
{% endfor %}
displays almost exactly want I want. The only thing I want to change is I the list of attendees to be sorted by last name. I've tried saying something like this:
{% for event in events %}
{{ event.location }}
{% for attendee in event.attendee_set.order_by__last_name %}
{{ attendee.first_name }} {{ attendee.last_name }}
{% endfor %}
{% endfor %}
Alas, the above syntax doesn't work (it produces an empty list) and neither does any other variation I have thought of (lot's of syntax errors reported, but no joy).
I could, of course, produce some kind of array of sorted attendee lists in my view, but that is an ugly and fragile (and did I mention ugly) solution.
Needless to say, but I'll say it anyway, I have perused the on-line docs and searched Stack Overflow and the archives of django-user without finding anything helpful (ah, if only a query set were a dictionary dictsort would do the job, but it's not and it doesn't)
==============================================
Edited to add additional thoughts
after accepting Tawmas's answer.
Tawmas addressed the issue exactly as I presented it -- although the solution was not what I expected. As a result I learned a useful technique that can be used in other situations as well.
Tom's answer proposed an approach I had already mentioned in my OP and tentatively rejected as being "ugly".
The "ugly" was a gut reaction, and I wanted to clarify what was wrong with it. In doing so I realized that the reason it was an ugly approach was because I was hung up on the idea of passing a query set to the template to be rendered. If I relax that requirement, there is an un-ugly approach that should work.
I haven't tried this yet, but suppose that rather than passing the queryset, the view code iterated through the query set producing a list of Events, then decorated each Event with a query set for the corresponding attendees which WAS sorted (or filtered, or whatever) in the desired way. Something like so:
eventCollection = []
events = Event.object.[filtered and sorted to taste]
for event in events:
event.attendee_list = event.attendee_set.[filtered and sorted to taste]
eventCollection.append(event)
Now the template becomes:
{% for event in events %}
{{ event.location }}
{% for attendee in event.attendee_list %}
{{ attendee.first_name }} {{ attendee.last_name }}
{% endfor %}
{% endfor %}
The downside is the view has to "actualize" all of the events at once which could be a problem if there were large numbers of events. Of course one could add pagination, but that complicates the view considerably.
The upside is the "prepare the data to be displayed" code is in the view where it belongs letting the template focus on formatting the data provided by the view for display. This is right and proper.
So my plan is to use Tawmas' technique for large tables and the above technique for small
tables, with the definition of large and small left to the reader (grin.)
You can use template filter dictsort https://docs.djangoproject.com/en/dev/ref/templates/builtins/#std:templatefilter-dictsort
This should work:
{% for event in eventsCollection %}
{{ event.location }}
{% for attendee in event.attendee_set.all|dictsort:"last_name" %}
{{ attendee.first_name }} {{ attendee.last_name }}
{% endfor %}
{% endfor %}
You need to specify the ordering in the attendee model, like this. For example (assuming your model class is named Attendee):
class Attendee(models.Model):
class Meta:
ordering = ['last_name']
See the manual for further reference.
EDIT. Another solution is to add a property to your Event model, that you can access from your template:
class Event(models.Model):
# ...
#property
def sorted_attendee_set(self):
return self.attendee_set.order_by('last_name')
You could define more of these as you need them...
One solution is to make a custom templatag:
#register.filter
def order_by(queryset, args):
args = [x.strip() for x in args.split(',')]
return queryset.order_by(*args)
use like this:
{% for image in instance.folder.files|order_by:"original_filename" %}
...
{% endfor %}
regroup should be able to do what you want, but is there a reason you can't order them the way you want back in the view?
How can you perform complex sorting on an object before passing it to the template? For example, here is my view:
#login_required
def overview(request):
physicians = PhysicianGroup.objects.get(pk=physician_group).physicians
for physician in physicians.all():
physician.service_patients.order_by('bed__room__unit', 'bed__room__order', 'bed__order')
return render_to_response('hospitalists/overview.html', RequestContext(request, {'physicians': physicians,}))
The physicians object is not ordered correctly in the template. Why not?
Additionally, how do you index into a list inside the template? For example, (this doesn't work):
{% for note_type in note_types %}
<div><h3>{{ note_type }}</h3>
{% for notes in note_sets.index(parent.forloop.counter0) %}
#only want to display the notes of this note_type!
{% for note in notes %}
<p>{{ note }}</p>
{% endfor %}
{% endfor %}
</div>
{% endfor %}
Thanks a bunch, Pete
As others have indicated, both of your problems are best solved outside the template -- either in the models, or in the view. One strategy would be to add helper methods to the relevant classes.
Getting a sorted list of a physician's patients:
class Physician(Model):
...
def sorted_patients(self):
return self.patients.order_by('bed__room__unit',
'bed__room__order',
'bed__order')
And in the template, use physician.sorted_patients rather than physician.patients.
For the "display the notes of this note_type", it sounds like you might want a notes method for the note_type class. From your description I'm not sure if this is a model class or not, but the principle is the same:
class NoteType:
...
def notes(self):
return <calculate note set>
And then the template:
{% for note_type in note_types %}
<div><h3>{{ note_type }}</h3></div>
{% for note in note_type.notes %}
<p>{{ note }}</p>
{% endfor %}
</div>
{% endfor %}
"I'd like to do this from within a template:"
Don't. Do it in the view function where it belongs.
Since the question is incomplete, it's impossible to guess at the data model and provide the exact solution.
results= physician.patients.order_by('bed__room__unit', 'bed__room__order', 'bed__order')
Should be sufficient. Provide results to the template for rendering. It's in the proper order.
If this isn't sorting properly (perhaps because of some model subtletly) then you always have this kind of alternative.
def by_unit_room_bed( patient ):
return patient.bed.room.unit, patient.bed.room.order, patient.bed.order
patient_list = list( physician.patients )
patient_list.sort( key=by_unit_room_bed )
Provide patient_list to the template for rendering. It's in the proper order.
"how do you index into a list inside the template"
I'm not sure what you're trying to do, but most of the time, the answer is "Don't". Do it in the view function.
The template just iterate through simple lists filling in simple HTML templates.
If it seems too complex for a template, it is. Keep the template simple -- it's only presentation. The processing goes in the view function
You should be able to construct the ordered query set in your view and pass it to your template:
def myview(request):
patients = Physician.patients.order_by('bed__room__unit',
'bed__room__order',
'bed__order')
return render_to_response('some_template.html',
dict(patients=patients),
mimetype='text/html')
Your template can then loop over patients which will contain the ordered results. Does this not work for you?
EDIT: For indexing, just use the dot syntax: mylist.3 in a template becomes mylist[3] in python. See http://docs.djangoproject.com/en/dev/ref/templates/api/#rendering-a-context for more information.
This is one way of doing it, although very ugly :
{% for note in note_sets|slice:"forloop.counter0"|first %}