change raw query into django models - django

i want to use django models feature to excute this sql query.
SELECT COUNT(DISTINCT ques_id), title FROM contest_assignment WHERE grp_id = 60 GROUP BY title;
i tried this but it did not give me proper result:
from django.db.models import Count
assignment.objects.values_list('title').annotate(count=Count('ques')).values('title', 'count')
how can i use django model?

You shouldn't use both .values() and .values_list(). The count field is implicitly added to the set of fields that is returned.
Django supports a COUNT(DISTINCT <field>) query by using .annotate(count=Count('<field>', distinct=True)). That would leave you with the following query:
(assignment.objects.filter(grp_id=60).values('title')
.annotate(count=Count('ques_id', distinct=True)))

Related

Django nested query in From clause

Is there any way to construct a query like the following using Django ORM?
SELECT * from ( SELECT r1 from table_name ) temp;
You can use Subquery as the docs says.
Here is an example:
from django.db.models import Subquery
Table.objects.filter(id__in=Subquery(Table2.objects.filter(...).values_list("table_id", flat=True)))

"SELECT DISTINCT field_name from table" Django using raw sql

How can I run SELECT DISTINCT field_name from table; SQL query in Django as raw sql ?
When I try to use Table.objects.raw("""SELECT DISTINCT field_name from table"""), I got an exception as
InvalidQuery: Raw query must include the primary key
If you don't need the model instances (which are useless if you want a single field), you can as well just use a plain db-api cursor:
from django.db import connection
cursor = connection.cursor()
cursor.execute("select distinct field from table")
for row in cursor:
print(row[0])
But for your example use case you don't need SQL at all - the orm has values and values_list querysets and a distinct() modifier too:
queryset = YourModel.objects.values_list("field", flat=True).order_by("field").distinct()
print(str(queryset.query))
# > 'SELECT DISTINCT `table`.`field` FROM `table` ORDER BY `table`.`title` ASC'
for title in queryset:
print(title)
NB :
1/ since we want single field, I use the flat=True argument to avoid getting a list of tuples
2/ I explicitely set the ordering on the field else the default ordering eventually defined in the model's meta could force the ordering field to be part of te generated query too.
Looks like you have to use some workaround
select field_name, max(id)
from table_name
group by field_name;

Django ORM form for the following sql query

I would like to know how to frame the following to the Django ORM form:
SELECT name,mobile_number,COUNT(*) as count, created FROM core_user,core_useractivity WHERE core_user.id = core_useractivity.user_id GROUP BY user_id ORDER BY count DESC;
where name, mobile number are columns in core_user table or USER model, and created is a column in core_useractivity its model is UserActivity . The two tables are joined on user_id. Further I am grouping the rows by user_id displaying the result in Descending order.
Try annotating the users with the user activity count.
from django.db.models import Count
User.objects.annotate(num_activities=Count('useractivity')).order_by('num_activities')
This will return a queryset of users, each with an attribute num_activities. See the docs on annotation for more information.

Django queryset to return first of each item in foreign key based on date

need to get a queryset with the first book (by a date field) for each author (related to by foreign key) ...is there a Django ORM way to do this (without custom SQL preferred but acceptable)
*Edit: Please note that an answer that works using only a modern open source backend like Postgresql is acceptable ..still ORM based solution preferred over pure custom sql query)
Models
class Book(Model):
date = Datefield()
author = ForeignKey(Author)
class Author(Model):
name = CharField()
Book.objects.filter(??)
If you use PostgreSQL or another DB backend with support for DISTINCT ON there is a nice solution:
Books.objects.order_by('author', '-date').distinct('author')
Otherwise I don't know a solution with only one query. But you can try this:
from django.db.models import Q, Max
import operator
books = Book.objects.values('author_id').annotate(max_date=Max('date'))
filters = reduce(operator.or_, [(Q(author_id=b['author_id']) &
Q(date=b['max_date'])) for b in books])
queryset = Books.objects.filter(filters)
With the combination of .values() and .annotate() we group by the author and annotate the latest date of all books from that author. But the result is a dict and not a queryset.
Then we build a SQL statement like WHERE author_id=X1 AND date=Y1 OR author_id=X2 AND date=Y2.... Now the second query is easy.

Finding multiple instances of a tag with Django "through" field

I run a lab annotation website where users can annotate samples with tags relating to disease, tissue type, etc. Here is a simple example from models.py:
from django.contrib.auth.models import User
from django.db import models
class Sample(models.Model):
name = models.CharField(max_length = 255)
tags=models.ManyToManyField('Tag', through = 'Annot')
class Tag(models.Model):
name = models.CharField(max_length = 255)
class Annot(models.Model):
tag = models.ForeignKey('Tag')
sample = models.ForeignKey('Sample')
user = models.ForeignKey(User, null = True)
date = models.DateField(auto_now_add = True)
I'm looking for a query in django's ORM which will return the tags in which two users agree on the annotation of same tag. It would be helpful if I could supply a list of users to limit my query (if someone only believes User1 and User2 and wants to find the sample/tag pairs that only they agree on.)
I think I understood what you need. This one made me think, thanks! :-)
I believe the equivalent SQL query would be something like:
select t.name, s.name, count(user_id) count_of_users
from yourapp_annot a, yourapp_tag t, yourapp_sample s
where a.tag_id = t.id
and s.id = a.sample_id
group by t.name, s.name
having count_of_users > 1
While I try hard not to think in SQL when I'm coming up with django model navigation (it tends to get in the way); when it comes to aggregation queries it always helps me to visualize what the SQL would be.
In django we now have aggregations.
Here is what I came up with:
models.Annot.objects.select_related().values(
'tag__name','sample__name').annotate(
count_of_users=Count('user__id')).filter(count_of_users__gt=1)
The result set will contain the tag, the sample, and the count of users that tagged said sample with said tag.
Breaking it apart for the folks that are not used to django aggregation:
models.Annot.objects.select_related()
select_related() is forcing all tables related to Annot to be retrieved in the same query
This is what will allow me to specify tag__name and sample__name in the values() call
values('tag__name','sample__name')
values() is limiting the fields to retrieve to tag.name and sample.name
This makes sure that my aggregation on count of clients will group by just these fields
annotate(count_of_users=Count('user__id'))
annotate() adds an aggregation as an extra field to a query
filter(count_of_users__gt=1)
And finally I filter on the aggregate count.
If you want to add an additional filter on what users should be taken into account, you need to do this:
models.Annot.objects.filter(user=[... list of users...]).select_related().values(
'tag__name','sample__name').annotate(
count_of_users=Count('user__id')).filter(count_of_users__gt=1)
I think that is it.
One thing... Notice that I used tag__name and sample__name in the query above. But your models do not specify that tag names and sample names are unique.
Should they be unique? Add a unique=True to the field definitions in the models.
Shouldn't they be unique? You need to replace tag__name and sample__name with tag__id and sample__id in the query above.