Reputation: 3954
This question is like django sorting but I can't make any of the solutions work.
I have the following model:
from django.db import models
from django.contrib.auth.models import User
class Product(models.Model):
title = models.CharField(max_length=200)
pub_date = models.DateTimeField()
body = models.TextField()
url = models.TextField()
image = models.ImageField(upload_to='images/')
icon = models.ImageField(upload_to='images/')
votes_total = models.IntegerField(default=1)
hunter = models.ForeignKey(User, on_delete=models.CASCADE)
def summary(self):
return self.body[:50]
def pub_date_pretty(self):
return self.pub_date.strftime('%b %e %Y')
def __str__(self):
return self.title
I would like to sort by votes_total so that when I loop through the database results are ordered by votes_total.
{% extends 'base.html' %}
{% block content %}
{% for product in products.all %}
<div class="row pt-3">
<div class="col-2" onclick="window.location='{% url 'detail' product.id %}';" style="cursor:pointer">
<img src="{{ product.icon.url }}" class="img-fluid" alt="">
</div>
<div class="col-6" onclick="window.location='{% url 'detail' product.id %}';" style="cursor:pointer">
<h1>{{ product.title }}</h1>
<p>{{ product.summary }}</p>
</div>
<div class="col-4" onclick="window.location='{% url 'detail' product.id %}';" style="cursor:pointer">
<a href="javascript:{document.getElementById('upvote{{ product.id }}').submit()}"><button class="btn btn-primary btn-lg btn-block"><span class="oi oi-caret-top"></span> Upvote {{ product.votes_total }}</button></a>
</div>
</div>
<form id="upvote{{ product.id }}" method="POST" action="{% url 'upvote' product.id %}">
{% csrf_token %}
<input type="hidden">
</form>
{% endfor %}
{% endblock %}
How do I sort my results?
Upvotes: 3
Views: 6691
Reputation: 476624
There are basically two ways you can do this:
The last option is typically used if the model has a sensical (inherent) ordering (for example it is very logical to order a queryset in a certain way, and only occasionally in another way).
We can sort a queryset, by using the .order_by(…)
[Django-doc] function of a queryset. The function takes an arbitrary number of parameters: strings that specify the column names. In case a column name is prefixed with a minus (-
) it means we sort in descending order.
For example we can define a view:
def some_view(request):
my_products = Product.objects.all().order_by('-votes_total')
return render(request, 'my_template.html', {'my_products': my_products})
then we can use this 'my_products'
variable, and iterate over it:
{% extends 'base.html' %}
{% block content %}
{% for product in my_products %}
<div class="row pt-3">
<div class="col-2" onclick="window.location='{% url 'detail' product.id %}';" style="cursor:pointer">
<img src="{{ product.icon.url }}" class="img-fluid" alt="">
</div>
<div class="col-6" onclick="window.location='{% url 'detail' product.id %}';" style="cursor:pointer">
<h1>{{ product.title }}</h1>
<p>{{ product.summary }}</p>
</div>
<div class="col-4" onclick="window.location='{% url 'detail' product.id %}';" style="cursor:pointer">
<a href="javascript:{document.getElementById('upvote{{ product.id }}').submit()}"><button class="btn btn-primary btn-lg btn-block"><span class="oi oi-caret-top"></span> Upvote {{ product.votes_total }}</button></a>
</div>
</div>
<form id="upvote{{ product.id }}" method="POST" action="{% url 'upvote' product.id %}">
{% csrf_token %}
<input type="hidden">
</form>
{% endfor %}
{% endblock %}
In case a certain ordering is quite logical from a model perspective, we can also encapsulate the ordering in the model itself. We specify this wth the ordering
attribute of the Meta
class of the attribute. In case we want for example to order Product.objects.all()
by default by the number of votes in descending order, we can write:
class Product(models.Model):
title = models.CharField(max_length=200)
pub_date = models.DateTimeField()
body = models.TextField()
url = models.TextField()
image = models.ImageField(upload_to='images/')
icon = models.ImageField(upload_to='images/')
votes_total = models.IntegerField(default=1)
hunter = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
ordering = ['-votes_total']
So now we can still access Product.objects.all
in the template, and the items will be sorted automatically.
Note however that we can define only one such ordering per model, and thus making a certain decision, can not be specified based on a particular view. In case you want to "override" the ordering, you need to user order_by
on the queryset.
Upvotes: 7
Reputation: 2974
You should sort the queryset
in your view and pass it to the template. For sorting it in ascending order
return render(request, template_name,{
'products': Product.objects.all().order_by('votes_total')
})
For sorting in descending order:
return render(request, template_name,{
'products': Product.objects.all().order_by('-votes_total')
})
You should use products
(not products.all) in your template.
Upvotes: 3