I have a versioned model which (simplified) looks a bit like:
Project(id, ref, version)
unique_together(ref, version)
Where id is the autogenerated primary key, ref is a random UUID and version is an integer incremented by my application. Each time I save the project I create a new instance, add 1 to the version and copy the ref to the new object.
The following SQL will give me back the latest version of every Project, by doing a subselect.
SELECT * FROM myapp_project WHERE (ref, version) IN
(SELECT ref, max(version) FROM myapp_project GROUP BY ref)
Alternatively (slightly simpler perhaps):
SELECT * from myapp_project p
WHERE p.version =
(SELECT max(version) FROM myapp_project p1 WHERE p1.ref = p.ref)
How do I achieve the same query using Django's ORM?
Edit: I've got as far as this -
foo = Project.objects.values('ref').annotate(version=Max('version'))
This gives me something that looks right if I inspect it. As soon as I try to get the id out with:
foo.values('id')
It seems to discard the original result and gives back all the rows.
Edit more:
Worked around it for now with .extra():
maxids = """id in (SELECT id from myapp_project p WHERE p.version =
(SELECT max(version) FROM myapp_project p1 WHERE p1.ref = p.ref))"""
Project.objects.all().extra(where=[maxids])
¶ Django allows using SQL subqueries.
In the Django QuerySet API, F() expressions are used to refer to model field values directly in the database. Let's say you have a Product class with a price field, and you want to increase the price of all products in 20%. A possible solution would be: products = Product. all() for product in products: product.
`OuterRef` is an "outer reference", a construct created to allow a reference to the query surrounding the current subquery.
A QuerySet is a collection of data from a database. A QuerySet is built up as a list of objects. QuerySets makes it easier to get the data you actually need, by allowing you to filter and order the data.
Using in
here is an example straight from the linked document
inner_qs = Blog.objects.filter(name__contains='Cheddar')
entries = Entry.objects.filter(blog__in=inner_qs)
The exact query that you are using isn't one that is supported by all databases that work with Django. For example while it works on postgresql it does not work on sqlite so you would have to change your approach to use the primary key instead of the unique_together key. Or use a join.
Update
the second query can be handled more easily but I will refrain from posting that here since that would seem like plagarizing @anand's answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With