Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django query with order_by, distinct and limit on Postgresql

I have the following :

class Product(models.Model):
    name = models.CharField(max_length=255)

class Action(models.Model):
    product = models.ForeignKey(Product)
    created_at = models.DateTimeField(auto_now_add=True)

I would like to retrieve the 10 most recent actions ordered by created_at DESC with distinct products.

The following is close to the result but still misses the ordering:

Action.objects.all().order_by('product_id').distinct('product_id')[:10]
like image 450
Charlesthk Avatar asked May 06 '15 17:05

Charlesthk


People also ask

How to use distinct() query method in Django?

We can use a distinct () query method at the end or in between queryset and with other regular Django query methods like all (), values (), etc… 3. Let's see some examples a). I created a simple model called user. b) Loaded the sample data to the user model and it looks like the below.

How to sort from Z to a using select distinct in PostgreSQL?

You can sort from Z to A using “DESC” after the “ORDER BY” statement. Step 4) The Select DISTINCT in PostgreSQL clause can be used to remove duplicate rows from the result. It keeps one row for each group of duplicates. Lets query Postgres Select Distinct id values from our table tutorials using distinct queries in PostgreSQL

How to restrict the number of Records returned by PostgreSQL SELECT query?

Step 5) You can use the PostgreSQL order by LIMIT clause to restrict the number of records returned by the SELECT query column names: Name of the columns whose value you want to retrieve FROM: The FROM clause defines one or more source tables for the SELECT Unique Postgres. Distinct in PostgreSQL help you to remove duplicates from the result.

Can I use multiple databases in Django?

The Django Object-relational mapper is compatible with MySQL, PostgreSQL, SQLite, and Oracle. Remember that you can define the database of your project in the DATABASES setting of your project’s settings.py file. Django can work with multiple databases at a time, and you can program database routers to create custom routing schemes.


2 Answers

Your solution seems like it's trying to do too much. It will also result in 2 separate SQL queries. This would work fine and with only a single query:

action_ids = Action.objects.order_by('product_id', '-created_at')\
    .distinct('product_id').values_list('id', flat=True)

result = Action.objects.filter(id__in=action_ids)\
    .order_by('-created_at')[:10]
like image 126
Ross Lote Avatar answered Sep 29 '22 15:09

Ross Lote


EDIT: this solution works but Ross Lote's is cleaner

This is the way I finally did it, using Django Aggregation:

from django.db.models import Max
    
actions_id = Action.objects.all().values('product_id') \
    .annotate(action_id=Max('id')) \
    .order_by('-action_id')[:10] \
    .values_list('action_id', flat=True)

result = Action.objects.filter(id__in=actions_id).order_by('-created_at')

By setting values('product_id') we do a group by on product_id.

With annotate() we can use order_by only on fields used in values() or annotate(). Since for each action the created_at field is automatically set to now, ordering on created_at is the same as ordering on id, using annotate(action_id=Max('id')).order_by('-action_id') is the right way.

Finnaly, we just need to slice our query [:10]

Hope this helps.

like image 35
Charlesthk Avatar answered Sep 29 '22 15:09

Charlesthk