Group by in Django ORM - python

I want to get the number of "chats" between 2 users in my app and I have a table called "Message" that contains:
sender_user | reciever_user | contain | date
I want to do a query that gives me all the messages between 2 differents users, I know in SQL I need to use GROUP BY but how can I get the list of messages with the Django ORM? Do you have any idea?
NOTE: sender_user and reciever_user are instances of User table)

You don't need to "GROUP BY" in the case described. You need to filter on sender and receiver users - it's equivalent to an SQL "WHERE" clause. And you would also order by the date.
Message.objects.filter(sender_user='sender', receiver_user='receiver')

You can use annotate method of queryset. anotate method is abstruction for group by clauses in django.
Your query should probably be something like:
User.objects.filter(message_set__sender=me).annotate(message_count=Count('message'))
Reference: https://docs.djangoproject.com/en/1.7/topics/db/aggregation/#order-of-annotate-and-filter-clauses

Related

Django ORM Group By with Foreign Keys

user is a foreign key on tournament.
select u.id, u.display_name, count(t.id)
from tournament t join user u
on t.user_id = u.id
where date(t.start_date)> '2022-07-01'
group by u.display_name, u.id
How can I make the above SQL query work with django's ORM?
In the majority of cases trying to translate an sql query into Django ORM syntax isn't the way to go.
From what i understand, you want to count tournaments, filtered with a date, bound to an user.
Try something like:
UserModel.objects.annotate(tournament_count=Count("tournament", filter=Q(start_date__gt=my_date)))
The annotate method allows for additionnal columns to be present in the ResultSet moslty related or calculated ones. ("tournament" is name of your Tournament model, if you defined a related_name for the user FK, use this name instead)
If you really want a group by, take a look at this How to query as GROUP BY in django?

Django ORM PostgreSQL DELETE query

I have 30 instances of the Room objects, i.e. 30 rows in the database table.
In Python code I have Room.objects.all().delete().
I see that Django ORM translated it into the following PostgreSQL query: DELETE FROM "app_name_room" WHERE "app_name_room"."id" IN ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30).
Why doesn't the Django ORM use a more parsimonious DELETE FROM app_name_room query? Is there any way to switch to it and avoid listing all IDs?
Interesting question. It got me thinking so I went a little deeper. The main reason could be that using DELETE FROM app_name_room doesn't take care of CASCADE delete
However, answering your question
Is there any way to switch to it and avoid listing all IDs?
You can do this using the private method _raw_delete. For instance:
objects_to_delete = Foo.objects.all()
objects_to_delete._raw_delete(objects_to_delete.db)
This will execute the following query:
DELETE FROM "objects_to_delete"
PS: According to the function docstring:
Delete objects found from the given queryset in single direct SQL query. No signals are sent and there is no protection for cascades.

select in (select ..) using ORM django

How can I make a query
select name where id in (select id from ...)
using Django ORM? I think I can make this using some loop for for obtain some result and another loop for, for use this result, but I think that is not practical job, is more simple make a query sql, I think that make this in python should be more simple in python
I have these models:
class Invoice (models.Model):
factura_id = models.IntegerField(unique=True)
created_date = models.DateTimeField()
store_id = models.ForeignKey(Store,blank=False)
class invoicePayments(models.Model):
invoice = models.ForeignKey(Factura)
date = models.DateTimeField()#auto_now = True)
money = models.DecimalField(max_digits=9,decimal_places=0)
I need get the payments of a invoice filter by store_id,date of pay.
I make this query in mysql using a select in (select ...). This a simple query but make some similar using django orm i only think and make some loop for but I don't like this idea:
invoiceXstore = invoice.objects.filter(local=3)
for a in invoiceXstore:
payments = invoicePayments.objects.filter(invoice=a.id,
date__range=["2016-05-01", "2016-05-06"])
You can traverse ForeignKey relations using double underscores (__) in Django ORM. For example, your query could be implemented as:
payments = invoicePayments.objects.filter(invoice__store_id=3,
date__range=["2016-05-01", "2016-05-06"])
I guess you renamed your classes to English before posting here. In this case, you may need to change the first part to factura__local=3.
As a side note, it is recommended to rename your model class to InvoicePayments (with a capital I) to be more compliant with PEP8.
Your mysql raw query is a sub query.
select name where id in (select id from ...)
In mysql this will usually be slower than an INNER JOIN (refer : [http://dev.mysql.com/doc/refman/5.7/en/rewriting-subqueries.html]) thus you can rewrite your raw query as an INNER JOIN which will look like 1.
SELECT ip.* FROM invoicepayments i INNER JOIN invoice i ON
ip.invoice_id = i.id
You can then use a WHERE clause to apply the filtering.
The looping query approach you have tried does work but it is not recommended because it results in a large number of queries being executed. Instead you can do.
InvoicePayments.objects.filter(invoice__local=3,
date__range=("2016-05-01", "2016-05-06"))
I am not quite sure what 'local' stands for because your model does not show any field like that. Please update your model with the correct field or edit the query as appropriate.
To lean about __range see this https://docs.djangoproject.com/en/1.9/ref/models/querysets/#range

Django GROUP_BY through .annotate()

I have table invoices with field customer_id and some others fields. I need select count of purchases, taken by each user. In SQL it's should looks like this:
SELECT username, COUNT('customer_id') FROM `invoices`
LEFT JOIN `auth_user` ON `auth_user`.id = `invoices`.customer_id
GROUP BY `customer_id`, username
In Django i try:
Invoice.objects.annotate(buy_count=Count('customer')).all()
But this code groups by invoices.id instead of invoices.customer_id and returns wrong result.
I think you should turn it around, something like:
Customer.objects.annotate(buy_count=Count('invoice')).all()
There you'd get a list of Customer with their count of invoice.

How to filter records before executing annotation on those records in django models?

In documentation is example:
Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)
How can I filter authors, before executing annotation on authors?
For example I want Count only those authors that have name "John".
I don't believe you can make this selective count with the Django database-abstraction API without including some SQL. You make additions to a QuerySet's SQL using the extra method.
Assuming that the example in the documention is part an app called "inventory" and using syntax that works with postgresql (you didn't specify and it's what I'm more familiar with), the following should do what you're asking for:
Book.objects.extra(
select={"john_count":
"""SELECT COUNT(*) from "inventory_book_authors"
INNER JOIN "inventory_author" ON ("inventory_book_authors"."author_id"="inventory_author"."id")
WHERE "inventory_author"."name"=%s
AND "inventory_book_authors"."book_id"="inventory_book"."id"
"""},
select_params=('John',)
)

Categories