I have a couple models
class Order(models.Model):
user = models.ForeignKey(User)
class Lot(models.Model):
order = models.ForeignKey(Order)
buyer = models.ForeignKey(User)
What I'm trying to do is to annotate Lot objects with a number of buys made by a given user to the same seller. (it's not a mistake, Order.user is really a seller). Like “you’ve bought 4 items from this user recently”.
The closest I get was
recent_sold_lots = Lot.objects.filter(
order__user_id=OuterRef('order__user_id'),
status=Lot.STATUS_SOLD,
buyer_id=self.user_id,
date_sold__gte=now() - timedelta(hours=24),
)
qs = Lot.objects.filter(
status=Lot.STATUS_READY,
date_ready__lte=now() - timedelta(seconds=self.lag)
).annotate(same_user_recent_buys=Count(Subquery(recent_sold_lots.values('id'))))
But it fails when recent_sold_lots count is more than one: more than one row returned by a subquery used as an expression.
.annotate(same_user_recent_buys=Subquery(recent_sold_lots.aggregate(Count('id'))) doesn't seem to work also: This queryset contains a reference to an outer query and may only be used in a subquery.
.annotate(same_user_recent_buys=Subquery(recent_sold_lots.annotate(c=Count('id')).values('c')) is giving me Expression contains mixed types. You must set output_field.. If I add output_field=models.IntegerField() to the subquery call, it throws more than one row returned by a subquery used as an expression.
I'm stuck with this one. I feel I'm close to the solution, but what am I missing here?
The models you defined in the question do not correctly reflect the query you are making. In any case i'll use the model as a reference to my query.
from django.db.models import Count
user_id = 123 # my user id and also the buyer
buyer = User.objects.get(pk=user_id)
Lot.objects.filter(buyer=buyer).values('order__user').annotate(unique_seller_order_count=Count('id'))
What the query does is:
Filters the lot objects to the ones you have bought
Groups the Returned lots into the user who created the order
Annotates/Counts the responses for each group
Related
hey guys let's say I have these models
class Object1:
.....
class Object2:
user = models.ForeignKey(User)
rel = models.ForeignKey(Object1, related_name = 'objects')
isCOmpleted = models.BooleanField()
and I wanted to perform this query:
Object1.objects.all().prefetch_related(Prefetch('objects', Object2.objects.filter(user = specific_user))).annotate(is_completed=F('objects__isCompleted'))
and the user is only related to one object from the Object2, but I got duplicate objects in my query, and I know the reason is that I have two objects from Object2 in my database for two different users, but the problem is that F expression didn't look in my prefetched query using prefetch_related method, I tried the exact same thing in the shell and it's giving me the results that I have expected but not in the view, so what's the problem here exactly any help would be really appreciated
So I am using Django to construct a Query and I have 3 models as defined:
class Book(models.Model):
...
class Upload(models.Model):
...
book = models.ForeignKey(Book, on_delete=models.CASCADE)
class Region(models.Model):
...
page = models.ForeignKey(Upload, on_delete=models.CASCADE)
Given these 3 models I wanted a query that lists all the books and annotate them with a segmented_pages variable that contains the count of all the Upload that have non-zero number of regions.
Basically, counting the number of uploads per book that have atleast one region.
I am assuming the basic structure of the query would look like this and mainly the logic inside filter needs to be modified as there is no convenient count lookup.
Book.objects.annotate(segmented_pages=Count('upload', filter=Q(upload__region__count__gt=0))
Can someone please help me with the logic of the filter and a simple explanation of how to go about designing these types of queries using django models?
You can rewrite "non-zero number of regions" as "In the join produced by the query, the region for any upload must not be null", hence you can simply use isnull [Django docs]:
from django.db.models import Count, Q
Book.objects.annotate(
segmented_pages=Count(
'upload',
filter=Q(upload__region__isnull=False),
distinct=True
)
)
I have activity logs for user activities, basically structured like this:
class ActivityLog(TimeStampedModel):
user = models.ForeignKey(User, on_delete=models.CASCADE)
action_type = models.CharField(max_length=25)
object_raw = models.CharField(max_length=500)
I want to aggregate all the entries where object_raw matches, i.e. so if a user searched for 'foo' on 4 different occasions, I get back one entry for 'foo', with count=4. I'm having trouble doing this right now. I know how to do it in sql, but don't understand that django syntax. I've been reading through the docs but I still don't get it. If anyone could help, it would be much appreciated!
To get one object with 'foo' and how many objects have object_raw='foo' you can do:
activity_logs = ActivityLog.objects.filter(object_raw='foo')
if activity_logs.exists():
activity_logs.first() # get one object
activity_logs.count() # get number of objects
If you just want how many objects have object_raw='foo', you can use conditional expressions with aggregates:
from django.db.models import IntegerField, Sum
Client.objects.aggregate(
num_object_raw=Sum(
Case(
When(object_raw='foo', then=1),
output_field=IntegerField()
)
)
)
I have two models in Django. First model is User, the second model is Action. Action has foreign key to the User. Basically what I want to do is get users ordered by how many likes (actions) they did.
I would like to not change the models, because I have another functionality build upon it.
class User(models.Model):
is_superuser = models.BooleanField(default=False)
...
class Action(models.Model):
TYPES = (
("like", "User liked something"),
("comment", "User commeted on something"),
)
user = models.ForeignKey('User')
action = models.CharField(max_lenght=20, choices=TYPES)
This is how I was thinking to do this:
I need to filter the User model with some condition, this condition is quite complicated, but for the sake of an example say I want only superusers
then I would like to get only like actions from Action model for each user, that is only actions which have type equals to the "like". I don't want to filter users here, just filter the actions.
I want to group the actions by user_id and get their count for each user
then I would like to use the count to order the users
This is what I've tried:
users = User.objects.filter(is_superuser=True)
actions = Action.objects.filter(action="like", user_id__in=users).values('user') \
.annotate(action_count=models.Count('user'))
No idea what to do next. Maybe something like:
users.extra(select={'user_actions': actions}).order_by('user_actions')
But this doesn't work and results in ProgrammingError: syntax error at or near "[".
EDIT:
To make it more clear this is equivalent SQL of what I want to do:
SELECT * FROM user
INNER JOIN (SELECT action.user_id, COUNT(action.user_id) as action_count FROM action
WHERE action.action = "like" GROUP BY action.user_id) as ac
ON user.id = ac.user_id WHERE user.is_superuser = 1 ORDER BY ac.action_count DESC;
However I don't want to use SQL, because my condition on the User model is extremly complex and includes other joins as well, so it's better to do it using the ORM.
I have 2 models in my Django code:
class ModelA(models.Model):
name = models.CharField(max_length=255)
description = models.CharField(max_length=255)
created_by = models.ForeignKey(User)
class ModelB(models.Model):
category = models.CharField(max_length=255)
modela_link = models.ForeignKey(ModelA, 'modelb_link')
functions = models.CharField(max_length=255)
created_by = models.ForeignKey(User)
Say ModelA has 100 records, all of which may or may not have links to ModelB
Now say I want to get a list of every ModelA record along with the data from ModelB
I would do:
list_a = ModelA.objects.all()
Then to get the data for ModelB I would have to do
for i in list_a:
i.additional_data = i.modelb_link.all()
However, this runs a query on every instance of i. Thus making 101 queries to run.
Is there any way of running this all in just 1 query? Or at least less than the 101 queries.
I've tried putting in ModelA.objects.select_related().all() but this didn't seem to have any effect.
As Ofri says, select_related only works on forwards relations, not reverse ones.
There's no built-in way to automatically follow reverse relations in Django, but see my blog post for a technique to do it reasonably efficiently. The basic idea is to get all the related objects for every item at once, then associate them manually with their related item - so you can do it in 2 queries rather than n+1.
Django ORM is a good thing but some some things is better to do manually.
You may import connection cursor and execute raw sql in single query.
from django.db import connection
cur=connection.cursor()
cur.execute(query)
rows = cur.fetchall()
your query should look like (for MySQL)
SELECT * FROM appname_modela INNER JOIN appname_modelb ON appname_modela.id=appname_modelb.modela_link_id
The reason .select_related() didn't work, is that .select_related() is used to follow foreign keys. Your ModelA doesn't have a foreign key to ModelB. Its ModelB that has a foreign key to ModelA. (so a ModelA instance can have multiple ModelB instances related to it).
You could use this to do it in 2 queries, and a bit of python code:
list_b = ModelB.objects.all()
list_a = ModelA.objects.all()
for a in list_a:
a.additional_data = [b for b in list_b if b.modela_link_id==a.id]
from django.db.models import OuterRef, Subquery
newest = ModelB.objects.filter(modela_link=OuterRef('pk'))
ModelA.objects.annotate(newest=Subquery(newest))
https://docs.djangoproject.com/en/3.2/ref/models/expressions/#subquery-expressions