Can't query the sum of values using aggregators - django

I want to sum the values of all existing rows grouping by another field.
Here's my model structure:
class Answer(models.Model):
person = models.ForeignKey(Person)
points = models.PositiveIntegerField(default=100)
correct = models.BooleanField(default=False)
class Person(models.Model):
# irrelevant model fields
Sample dataset:
Person | Points
------ | ------
4 | 90
3 | 50
3 | 100
2 | 100
2 | 90
Here's my query:
Answer.objects.values('person').filter(correct=True).annotate(points_person=Sum('points'))
And the result (you can see that all the person values are separated):
[{'person': 4, 'points_person': 90}, {'person': 3, 'points_person': 50}, {'person': 3, 'points_person': 100}, {'person': 2, 'points_person': 100}, {'person': 2, 'points_person': 90}]
But what I want (sum of points by each person):
[{'person': 4, 'points_person': 90}, {'person': 3, 'points_person': 150}, {'person': 2, 'points_person': 190}]
Is there any way to achieve this using only queryset filtering?
Thanks!

Turns out I had to do the inverse filtering, by the Person's and not the Answers, like so:
Person.objects.filter(answer__correct=True).annotate(points=Sum('answer__points'))
Now I get the total summed points for each person correctly.

Related

How to get top 5 records in django dict data

I m having two tables 1) Visit 2) disease. visit table having a column for disease. I m trying to get top 5 disease from visit table.
dis=disease.objects.all()
for d in dis:
v=visits.objects.filter(disease=d.disease_name).count()
data={
d.disease_name : v
}
print (data)
This print all disease with respective count. as below:
{'Headache': 2}
{'Cold': 1}
{'Cough': 4}
{'Dog Bite': 0}
{'Fever': 2}
{'Piles': 3}
{'Thyroid': 4}
{'Others': 9}
I want to get top 5 from this list based on count. How to do it?
Thank you all for your reply, I got an other simple solution for it.
from django.db.models import Count
x = visits.objects.values('disease').annotate(disease_count=Count('disease')).order_by('-disease_count')[:5]
print(x)
it returns as below:
<QuerySet [{'disease': 'Others', 'disease_count': 9}, {'disease': 'Thyroid', 'disease_count': 4}, {'disease': 'Cough', 'disease_count': 4}, {'disease': 'Piles', 'disease_count': 3}, {'disease': 'Headache', 'disease_count': 2}]>
I think this is simplest solutions. It working for me...
Add data in a list and sort list based on what you want:
dis=disease.objects.all()
l = list()
for d in dis:
v=visits.objects.filter(disease=d.disease_name).count()
data={
d.disease_name : v
}
l.append(data)
l.sort(reverse=True, key=lambda x:list(x.values())[0])
for i in range(min(len(l), 5)):
print(l[i])
You can sort these values by writing code like that:
diseases = list(Disease.objects.values_list('disease_name', flat=True))
visits = list(
Visits.objects.filter(disease__in=diseases).values_list('disease', flat=True))
data = {}
for name in diseases:
count = visits.count(name)
data[name] = count
sorted_data = sorted(data.items(), key=operator.itemgetter(1), reverse=True)
new_data = {}
for idx in range(min(len(sorted_data), 5)):
item = sorted_data[idx]
new_data[item[0]] = item[1]
print(new_data)
It's little messy, but it does the job:
I also optimised your queries, so the code should also run bit faster (when you do logic like that, use list and .values_list(...) because it caches data in memory - and using native python functions on list instead of QuerySet like .count() should also be faster than hitting database).

Get Count of order on every day of week

Hi I have a model like :
class Order(models.Model):
user=models.ForeginKey(User)
startDatatime = models.DateTimeField(blank=True, null=True)
I want count orders on Each week day . like On all Mondays count of orders and on all week days etc
EDIT
Is that a good way to do that ?
data = []
weekdays = {'Sunday': 1, 'Monday': 2, 'Tuesday': 3, 'Wednesday': 4, ' Thursday': 5,
'Friday': 6, 'Saturday': 7}
for day, value in weekdays.items():
weekly_tasks_count = Task.objects.filter(
todo_list__for_user=request.user,
creation_date_time__week_day=value).count()
data.append({day: weekly_tasks_count})
You can use list comprehensions to tidy up your code a little bit more.
WEEKDAYS = {
'Sunday': 1,
'Monday': 2,
'Tuesday': 3,
'Wednesday': 4,
'Thursday': 5,
'Friday': 6,
'Saturday': 7
}
data = [
{
day: Task.objects.filter(
todo_list__for_user=request.user,
creation_date_time__week_day=value).count()
)}
for day, value in WEEKDAYS.items()
]
One more possible improvement is to build up the WEEKDAYS dictionary from values found in the datetime module, but so far having it defined your way doesn't harm readability.

How to group by over related model count in Django ORM

Let's say I have the following models:
class Conversation(Model):
...
class Message(Model):
conversation = models.ForeignKey(Conversation)
The following code counts the number of messages for each conversation:
Conversation.objects \
.filter(...) \
.annotate(size=Count("message")) \
.values("size")
and returns a query set of {'size': 0}, {'size': 1}, {'size': 0}, {'size': 0} etc.
But how do I aggregate over the conversation sizes? That is, obtaining a result something like this: (0, 3), (1, 1), where the first element in each pair is the conversation size and the second element is the number of conversations of this size?

Break django values down into count of each value

I have a model defined similar to below
class MyModel(models.Model):
num_attempts = models.IntegerField()
num_generated = models.IntegerField()
num_deleted = models.IntegerField()
Assuming my data looked something like this:
|id|num_attempts|num_generated|num_deleted
1 1 2 0
2 2 0 1
3 3 2 1
4 3 1 2
I want to get a count of the instances at each possible value for each possible field.
For example, a return sample could look like this.
{
'num_attempts_at_1': 1,
'num_attempts_at_2': 1,
'num_attempts_at_3': 2,
'num_generated_at_0': 1,
'num_generated_at_1': 1,
'num_generated_at_2': 2,
'num_deleted_at_0': 1,
'num_deleted_at_1': 2,
'num_deleted_at_2': 1
}
This above example assumes a lot, like naming of the variables after and that it would be serialized. None of that matters but rather just how do I get it broken down like that from the database. It would be best to have this done in one query if possible.
We are using Postgres as the database.
Here is sorta close, but not quite.
qs.values_list('num_attempts','num_generated','num_deleted').annotate(Count('id'))
Gives this (not the same data as the example above)
[{'num_attempts': 4, 'id__count': 3, 'num_deleted': 3, 'num_generated': 6}, {'num_attempts': 3, 'id__count': 12, 'num_deleted': 2, 'num_generated': 2}, {'num_attempts': 2, 'id__count': 5, 'num_deleted': 0, 'num_generated': 6}]
Now with some custom python I was able to do this, but really want a database solution if possible.
def get(self, request, *args, **kwargs):
qs = self.get_queryset()
return_data = {}
for obj in qs:
count = obj.pop('id__count')
for k, v in obj.items():
key = "{}_at_{}".format(k, v)
value = return_data.get(key, 0) + count
return_data[key] = value
return Response(return_data)

Complex Django Query to Create Dictionary

models.py
class Event(models.Model):
name = models.CharField(max_length=100)
date = models.DateField()
class Result(models.Model):
event = models.ForeignKey(Event)
place = models.IntegerField()
person = models.CharField(max_length=100)
gender = models.CharField(max_length=1)
score = models.IntegerField()
Event sample data:
id, name, date
1, 'event1', '2015-01-01'
2, 'event2', '2015-02-01'
3, 'event3', '2015-03-01'
Result sample data:
event_id, place, person, gender, score
1, 1, 'al', 'M', 25
1, 2, 'bob', 'M', 22
1, 3, 'cindy', 'F', 21
1, 4, 'doug', 'M', 20
2, 1, 'elen', 'F', 30
2, 2, 'frank', 'M', 28
2, 3, 'gord', 'M', 20
2, 4, 'helen', 'F', 19
I want to query this and get a dictionary containing the male and female winners (with scores) for each event:
winnersdict = {event_id: (mwinner, mscore, fwinner, fscore), ...}
In this case the resulting dictionary would be:
{1: ('al', 25, 'cindy', 21), 2: ('frank', 28, 'elen', 30). 3: (None, None, None, None)}
Right now I am doing it like this:
events = Event.objects.all().order_by('date')
winnersdict = {}
for e in events:
femalewinner = Result.objects.filter(event_id=e.id, gender='F')[:1]
if len(femalewinner) == 0:
fwinner = None
fscore = None
else:
fwinner = femalewinner[0].person
fscore = femalewinner[0].score
malewinner = Result.objects.filter(event_id=e.id, gender='M')[:1]
if len(malewinner) == 0:
mwinner = None
mscore = None
else:
mwinner = malewinner[0].person
mscore = malewinner[0].score
winnersdict[e.id] = (mwinner, mscore, fwinner, fscore)
Surely there is a smarter way. I'm going to have thousands of events, looping through this way seems terrible. If it simplified things I would also be fine with generating a separate femalewinnersdict and malewinnersdict. I'm also fine (might even prefer) if we leave the None's out of the resulting dictionary, for this case I'm not interested in events that don't have results yet.
Any ideas?
Please try following query, it would give you male/female separately.
from django.db.models import Max
male_results = Event.objects.filter(result__gender='M') \
.annotate(max_score=Max('result__score')) \
.values('name', 'result__person', 'result__score')