Modifying QuerySet result - django

Is it possible to change some specific items in a QuerySet object? In my case i'm trying to slicing "title" fields with length more than 40 characters and append "..." at the end of field.

There are 2 ways of doing what you want.
The first is to use a Django filter. So if you are looping through the items of your queryset and displaying them on a page use something like truncatewords. You would use this like this in your template:
{% for item in queryset %}
<h1>{{ item.title|truncatewords:3 }}</h1>
{% endfor %}
It doesn't look like there is a Django filter for truncating base on the number of characters. If you want to write your own filter it's not that hard to do.
The other option is to put a method on your model to do what you want. Here is an example:
#property
def short_title(self):
return '%s...' % self.title[:40]
You would then be able to reference this anywhere in your template as {{ object.short_title }}.

I suggest adding a new property 'adjusted_title' to each object
for item in your_query_set:
if(len(item.title) > 40):
item.adjusted_title = item.title[0:40] + "..."

Related

How to generate a total sum with a Django QuerySet and display in a template?

I'm using annotate with my Django queryset to count a number of records. I am using it with filter and understand the order makes a difference.
My query is: numcontestants = Contestant.objects.filter(assigned_team__id=urlid).annotate(num_contestants=Count('id'))
When I pass numcontestants to my template and display it with {{ numcontestants }} I see a queryset containing each of the records I want to view. When I try with:
{{ numcontestants.num_contestants }} I get nothing.
I don't want to have to iterate over numcontestants to get a count, my understanding was that num_contestants should be a single integer number. Even so, iterating over doesn't give the result I am after, for example using:
{% for x in numcontestants %} {{x.num_contestants}} {% endfor %}
outputs 1 1 1 when I am trying to get something that will output 3.
What is the right way to generate and access a sum count?
annotate return a number for each record, you can get num_contestants for the first record:
{{ numcontestants.0.num_contestants }}
if you want to get a number of queryset results can use .count() in your quesyset
numcontestants = Contestant.objects.filter(assigned_team__id=urlid).count()

Display a fixed-length subset of a list in a Django template

I'd like to display a fixed-length subset of a set of related objects in a Django template. For example, imagine that I have a Car, which has related object Owner. I would like to display the three most recent owners, but also always display three entries even if there are fewer. So
Ford Fiesta AA11 1AA
1. John Smith
2. Jane Smith
3. Jenny Smith
Aston Martin DB9
1. Richard Rich
2.
3.
even if the Fiesta has had 10 owners (and the DB9 has had only one).
The naive way to do this would be
<h1>{{car.name}}</h1>
<ol>
{% for owner in car.owner_set|slice:":3" %}
<li>{{owner.name}}</li>
{% endfor %}
</ol>
but this will only display one list item if there has only been one owner.
I could also add lines like
{% if car.owner_set|length < 2 %}<li></li>{% endif %}
{% if car.owner_set|length < 3 %}<li></li>{% endif %}
but that's terrible. Is there a nicer way to do this?
The easiest way seems to me to be to define a new filter.
Create a templatetags package in the relevant app and add a file called liststuff.py (or whatever name you wish) with the following code:
from django import template
register = template.Library()
#register.filter
def exactlength(listlike_thing, desired_length):
list_thing = list(listlike_thing)
if len(list_thing) < desired_length:
# pad the list with as many empty strings as needed
list_thing += ([''] * (desired_length - len(list_thing)))
return list_thing[:desired_length]
Then, at the top of your template, load that file (if you called your file something else, change this line accordingly):
{% load liststuff %}
and in your loop initialisation, pass your values through the filter:
{% for owner in car.owner_set|exactlength:3 %}
You could create a custom filter or similar, but for just the one instance, I would add a method to the Car model that returned what you're after, something like:
def three_latest_owners(self):
num = 3
latest = list(self.owner_set.all()[:num])
latest += [None] * num
return latest[:num]
Then in the template, {% for owner in car.three_latest_owners %}
Obviously, not the most elegant solution nor the most re-usable, but the real naive solution would surely be:
<h1>{{car.name}}</h1>
<ol>
<li>{{car.owner_set.0.name}}</li>
<li>{{car.owner_set.1.name}}</li>
<li>{{car.owner_set.2.name}}</li>
</ol>

Length of list in jinja template, where the list is the result of a DBSession.query

How do I find the length of a list of objects in a jinja template when the list of objects has been created by querying a database?
I thought There are {{ items|length }} items in this category. would work, but items is a result of:
items = db_session.query(Item).filter_by(category_id=category.id)
and I get the error
TypeError: object of type 'Query' has no len()
Obviously, I could calculate the length separately and pass into render_template(), but I wondered if there was a better way?
Any help from the community would be much appreciated :)
items object is not a list yet, it is an unprocessed Query object as you see in the error. You can use Query.all() method to get a list of items:
items = db_session.query(Item).filter_by(category_id=category.id).all()
After that length filter can be applicable.
Use {{ items | count }} in jinja template
Try to add loopcontrol extension
app.jinja_env.add_extension('jinja2.ext.loopcontrols')
Then
{% for item in items %}
{{loop.length}}
{% break %}
{% endfor %}

How to reverse a for loop in a Django template and then slice the result

In a Django template, I'm iterating over a set of photos, and displaying them one by one. Specifically, right now I just have one photo set, containing 6 objects. I display these 6 objects like so:
{% for pic in picstream.photo_set.all reversed %}
<img src="{{ pic.image_file.url }}"></img>
{% endfor %}
Adding reversed to the statement gives me the 6 objects in the desired ordering (i.e. the latest ids first).
Next, I want to display not more than 4 objects from the photo_set. I added |slice:":4" to picstream.photo_set.all to achieve this. Problem is, it's cutting off the first two objects from my desired oredering.
It seems there ought to have been a way to reverse the list first, and slice later? Need a simple way to do this, without performance compromises.
Instead of using the reversed argument for the for template tag, you can use the reverse method of the queryset itself:
{% for pic in picstream.photo_set.all.reverse|slice:":4" %}
<img src="{{ pic.image_file.url }}"></img>
{% endfor %}
If you are evaluating the original (non-reversed) queryset somewhere else in your code then this will result in a second query hitting the database. If this is the case then you are better off moving the logic into your view code itself or into a template tag.

Issues with Fetching the data from database

I can fetch the data like this.
value= mymodel.objects.get(anycondition)
OR
value= mymodel.objects.filter(anycondition)
and can send them to my template with context.
But if I want to select all the data from a table(for all users not only one) as this query does
value= mymodel.objects.all()
and send this value to my template and can see there field by field
e.g.
my table has two fields name and phone no and I use the above query( value= mymodel.objects.all()) now if i want to see all names then i can see that and if i want to see phone no. I can see that too.
I have tried this and it doesn't work and I even I do not know it is possible or not.
If it is possible then please let me know how I can do this ?
I hope you understand my question. !!
Thanks in advance
.all() will return a list of objects that represent the rows in your model. .get() only returns one object. Your template is trying to print the result of all() if it was one object.
This is the same as if you had a list and you wanted to loop through it. In your view you would do:
product = Product_attributes.objects.all()
for i in product:
print i.size
print i.color
The equvalent for the template is:
<ul>
{% for i in product %}
<li>{{ i.size }}</li>
<li>{{ i.color }}</li>
{% endfor %}
</ul>
Although this question isn't clear it seems like you are having a bit of problem with Field Lookups. It is fairly easy to learn Here is a link to get you started