'Django query for latest version of a object
I am trying to write a query in Django but I'm not sure whats the best way to write this. Also assuming that the database is mySQL. This matters if distinct is used.
class Homework(models.Model):
title = TextField()
class Assignment(models.Model):
homework_id = Foreignkey(Homework, on_delete=models.CASCADE)
task = IntegerField(default=1)
version = IntegerField(default=1)
Given the models above I want to get all the assignments in a particular homework where the assignments are the latest version of the task. example:
Homework_id: 1
assignment v1, t1
assignment v2, t1
assignment v1, t2
If I have one homework object where the assignment with task 1 and assignment task 2 are in it then the result query should return Assignment t1, v2 and Assignment t2, v1
v refers to version
t refers to task
Solution 1:[1]
You could try annotating the tasks with the max version number like this:
Assignment.objects.filter(homework_id=1).values('task').annotate(version=Max('version'))
Which would get you something like:
<QuerySet [{'task': 1, 'version': 2}, {'task': 2, 'version': 1}]>
Solution 2:[2]
I know some people might turn up their nose about what I am proposing, but after trying a lot of complicate and resource intensive approaches I came at this simple one that I will explain.
A big issue comes from the fact that you cannot query all database backends using distinct(field_name)
E.G.:
MyModel.objects.all().order_by('id', '-version').distinct('id')
So what I have done is simply adding a BooleanField
called latest
that will be set to True when the version is the latest and False otherwise.
Some considerations:
- When you add a record you should use a transaction and set the field to True to the record you are adding and False to all the others
- When you make a query you just filter using
.filter(latest=True)
and everything is a lot more easy and with good performances.
In the example code the django Model is subclassed to perform operations on save
.
from django.db import transaction
class MModel(models.Model):
@transaction.atomic
def save(self, *args, **kwargs):
cls = type(inst) # getting model
# This will reset the boolean field "latest" for all records
# with same used "myid"
qry_same_id = cls.objects.filter(myid=inst.myid)
for record in qry_same_id:
record.latest = False
record.save()
# at this point I set the record I am saving to be the latest
self.latest = True
super().save(*args, **kwargs)
class MyModel(MModel):
# Example model
myid = models.CharField(max_length=20)
version = models.IntegerField()
latest = models.BooleanField()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | damores |
Solution 2 | piertoni |