Convert a basic sql query to django ORM - python

I want to display the course name along with the question count in a table. Need help to convert below query to a django ORM:
SELECT DISTINCT exam_course.course_name,
COUNT(exam_question.question)
FROM exam_course
INNER JOIN exam_question ON exam_question.course_id = exam_course.id
GROUP BY exam_question.course_id

Use annotate with count as commented...use in this manner, replace accordingly your requirements:
invoices = Invoice.objects.annotate(total_amount=Sum('order__order_items__amount'),number_of_invoices=Count('pk', distinct=True))

Related

How to apply a filter for each row of the same queryset on Django

I have the a model Product which has a relation with itself using the key "parent".
This way, to get the parent of a product i just do product.parent and if i want the children of a product i do product.product_set.all().
What i want to do is to do an annotate on a queryset of parent products and sum the stock of each product with the stock of its children. Something similar to this:
qs = Product.objects.filter(is_parent=True)
qs = qs.annotate(stock_sum=Sum(Product.objects.filter(parent_id=F('id')).values_list['stock']))
Is this possible? Will it result in many queries or django know a way of handling this with a few joins?
Save for one syntax error (square brackets rather than parentheses after values_list) this should work as you intended.
This induces only a single query. Note you can check the SQL that the ORM generates by accessing the .query property of the queryset (the following was subsequently run through a SQL pretty-printer):
>>> str(qs.query)
SELECT
"yourapp_product"."id",
"yourapp_product"."parent_id",
"yourapp_product"."stock",
SUM((SELECT U0."stock" FROM "yourapp_product" U0 WHERE U0."parent_id" = (U0."id"))) AS "stock_sum"
FROM
"yourapp_product"
WHERE
"yourapp_product"."is_parent"
GROUP BY
"yourapp_product"."id",
"yourapp_product"."parent_id",
"yourapp_product"."stock";

Django ORM - Filter by Multiple Columns Ranges

I have a requirement to fetch the data (both i_week and i_year) for the last one year from current date. The table has the following columns. Let us call the table as temp.
i_week - The week number [1,52]
i_year - The year
Other columns
I know how to fetch the data using SQL (Postgres). This is the query:
SELECT "i_week", "i_year" FROM "temp" WHERE ("i_week", "i_year") BETWEEN (1, 2021) AND (52, 2022);
What I have tried so far using Django ORM is this:
temp.objects.filter(i_week__range=(previous_week, current_week), i_year__range=(previous_year, current_year))
But this is not giving me the desired result.
How can I achieve the above SQL query using Django ORM ? How can I filter the ranges of i_week and i_year at once in ORM ?
let's just mention the Q object method. You can try the ORM query this way:
from django.db.models import Q
temp.objects.filter(
Q(i_week__range=(previous_week, current_week)) &
Q(i_year__range=(previous_year, current_year))
)

How to write subquery in django

Is it possible to make following sql query in django
select * from (
select * from users
) order by id
It is just minimal example. I have a long subquery instead of select * from users. But I can't understand how insert it into subquery.
UPDATED:
Subquery from doc doesn't suits because it build following request
SELECT "post"."id", (
SELECT U0."email"
FROM "comment" U0
WHERE U0."post_id" = ("post"."id")
ORDER BY U0."created_at" DESC LIMIT 1
) AS "newest_commenter_email" FROM "post"
and this subquery can return only one value (.values('email')).
Construction select (subquery) as value from table instead of select value from (subquery)
i would use a python connector to postgreSQL - http://www.postgresqltutorial.com/postgresql-python/query/, that is what i do for the mysql, thought did not try for the postgresql
Making a subquery is essentially setting up two queries and using one query to "feed" another:
from django.db.models import Subquery
all_users = User.objects.all()
User.objects.annotate(the_user=Subquery(all_users.values('email')[:1]))
This is more or less the same as what you provided. You can get about as complicated as you'd like here but the best source to get going with subqueries is the docs

Django ORM: Get latest record for distinct field

I'm having loads of trouble translating some SQL into Django.
Imagine we have some cars, each with a unique VIN, and we record the dates that they are in the shop with some other data. (Please ignore the reason one might structure the data this way. It's specifically for this question. :-) )
class ShopVisit(models.Model):
vin = models.CharField(...)
date_in_shop = models.DateField(...)
mileage = models.DecimalField(...)
boolfield = models.BooleanField(...)
We want a single query to return a Queryset with the most recent record for each vin and update it!
special_vins = [...]
# Doesn't work
ShopVisit.objects.filter(vin__in=special_vins).annotate(max_date=Max('date_in_shop').filter(date_in_shop=F('max_date')).update(boolfield=True)
# Distinct doesn't work with update
ShopVisit.objects.filter(vin__in=special_vins).order_by('vin', '-date_in_shop).distinct('vin').update(boolfield=True)
Yes, I could iterate over a queryset. But that's not very efficient and it takes a long time when I'm dealing with around 2M records. The SQL that could do this is below (I think!):
SELECT *
FROM cars
INNER JOIN (
SELECT MAX(dateInShop) as maxtime, vin
FROM cars
GROUP BY vin
) AS latest_record ON (cars.dateInShop= maxtime)
AND (latest_record.vin = cars.vin)
So how can I make this happen with Django?
This is somewhat untested, and relies on Django 1.11 for Subqueries, but perhaps something like:
latest_visits = Subquery(ShopVisit.objects.filter(id=OuterRef('id')).order_by('-date_in_shop').values('id')[:1])
ShopVisit.objects.filter(id__in=latest_visits)
I had a similar model, so went to test it but got an error of:
"This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery"
The SQL it generated looked reasonably like what you want, so I think the idea is sound. If you use PostGres, perhaps it has support for that type of subquery.
Here's the SQL it produced (trimmed up a bit and replaced actual names with fake ones):
SELECT `mymodel_activity`.* FROM `mymodel_activity` WHERE `mymodel_activity`.`id` IN (SELECT U0.`id` FROM `mymodel_activity` U0 WHERE U0.`id` = (`mymodel_activity`.`id`) ORDER BY U0.`date_in_shop` DESC LIMIT 1)
I wonder if you found the solution yourself.
I could come up with only raw query string. Django Raw SQL query Manual
UPDATE "yourapplabel_shopvisit"
SET boolfield = True WHERE date_in_shop
IN (SELECT MAX(date_in_shop) FROM "yourapplabel_shopvisit" GROUP BY vin);

Django query with AVG and GROUP BY

My Django-foo isn't quite up to par to translate certain raw sql into the ORM.
Currently I am executing:
SELECT avg(<value_to_be_averaged>), <id_to group_on>
FROM <table_name>
WHERE start_time >= <timestamp>
GROUP BY <id_to group_on>;
In Django I can do:
Model.objects.filter(start_time__gte=<timestamp>).aggregate(Avg('<value_to_be_averaged>'))
but that is for all objects in the query and doesn't return a query set that is grouped by the id like in the raw SQL above. I've been fiddling with .annotate() but haven't made much progress. Any help would be appreciated!

Categories

Resources