Reputation: 487
Given a Model in Django with fields/values as shown:
Group | Votes |
---|---|
A | 5 |
B | 2 |
C | 6 |
B | 7 |
A | 3 |
C | 4 |
it's easy to use
class Meta:
ordering = ['group', '-votes']
to return
Group | Votes |
---|---|
A | 5 |
A | 3 |
B | 7 |
B | 2 |
C | 6 |
C | 4 |
How could this be modified so that the Group
is also ordered by the max Votes
value within each Group
as shown:
Group | Votes |
---|---|
B | 7 |
B | 2 |
C | 6 |
C | 4 |
A | 5 |
A | 3 |
Upvotes: 0
Views: 146
Reputation: 1586
You may use Window functions which will let you to calculate maximum per group for each record and later use it for sorting, like this:
YourModel.objects.annotate(
max_per_group = Window(
expression = Max('votes'),
partition_by = F('group')
)
).order_by(
'-max_per_group','group','-votes'
)
But if you need to place all this inside your model you should use Custom Manager, override get_queryset
add Window annotation to queryset and use the annotated value in Meta's ordering, like this:
class YourModelObjectsManager(models.Manager):
def get_queryset(self):
return models.Manager.get_queryset(self).annotate(
max_per_group = Window(
expression = Max('votes'),
partition_by = F('group')
)
)
class YourModel(models.Model):
....
objects = YourModelObjectsManager()
class Meta:
ordering = ['-max_per_group','group','-votes']
But, please, pay attention that including this kind of annotation in your model will lead to additional calculations on the database side each time you access your model (and this will be more significant if your model contains a lot of records). Thus, if ordering is a special case i.e. you need it for example in only one view or just in Django's admin, I would recommend to use annotate
and order_by
for those special cases.
Upvotes: 1