[Django]-Sort by field value in Django 2.0

7👍

There are basically two ways you can do this:

  1. define the ordering in a view; or
  2. define a default ordering (an ordering that can be overridden with another ordering) in the model.

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

Sorting in a view

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 %}

Define an ordering on a model

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.

3👍

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.

Leave a comment