How to find total of all the estimates in query in Django - django

I have models like this:
class Person(models.Model):
first_name = models.CharField(max_length=60)
last_name = models.CharField(max_length=60)
class Ticket(models.Model):
assigned_to = models.ForeignKey(Person, null=True, blank=True)
estimate = models.DecimalField(null=True, blank=True, decimal_places=1, max_digits=4)
I want to find the total of all estimates for all tickets that are assigned to a particular person.
I am trying to do:
for i in Person.objects.all():
Ticket.objects.filter(assigned_to=i)
and use Sum() to add estimate() for each model.
But I am getting no data with the first query. I am sure I have datas. What's wrong?

You can do this entirely in the db with an aggregate.
from django.db.models import Sum
people_with_estimates = Person.objects.all().annotate(
estimate_total=Sum('ticket__estimate'))
Now each Person has an estimate_total attribute containing the sum of all their ticket estimates.

Assuming you know the person try this:
estimate_total = Decimal(0)
ticket_list = Ticket.objects.filter(assigned_to = person.id)
for i in range(len(ticket_list)):
estimate_total += ticket_list[i].estimate

Related

Django Sum in Annotate

Good afternoon,
I am really struggling with getting a sum using Annotate in DJango.
I am using User object and the following models:
class Depts(models.Model):
dept_name = models.CharField(max_length=55)
dept_description = models.CharField(max_length=255)
isBranch = models.BooleanField(default=False)
def __str__(self):
return "{}".format(self.dept_name)
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profile')
title = models.CharField(max_length=75)
dept = models.ForeignKey(Depts, on_delete=models.CASCADE, related_name="dept", null=True)
class ActivityLog(models.Model):
activity_datetime = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='activity_user')
activity_category = models.ForeignKey(ActivityCategory, on_delete=models.CASCADE, null=True, related_name='activity_cat')
activity_description = models.CharField(max_length=100, default="Misc Activity")
class ActivityCategory(models.Model):
activity_name = models.CharField(max_length=40)
activity_description = models.CharField(max_length=150)
pts = models.IntegerField()
def __str__(self):
return '%s' % (self.activity_name)
What I need to do is get a group of departments with aggregating the sum of the pts earned by all the users activitylogs.
So a user is part of department, they do activities, each activity is of a type activity_category and has associated points. How can I query using the ORM to get a sum of points for everyone in each department?
Thank you, I cannot seem to wrap my mind around it.
You annotate the departments with the sum:
from django.db.models import Sum
Depts.objects.annotate(
total_pts=Sum('dept__user__activity_user__activity_category__pts')
)
Note: The related_name=… parameter [Django-doc]
is the name of the relation in reverse, so from the Depts model to the UserProfile
model in this case. Therefore it (often) makes not much sense to name it the
same as the forward relation. You thus might want to consider renaming the dept relation to userprofiles.
After setting the related_name='userprofiles', the query is:
from django.db.models import Sum
Depts.objects.annotate(
total_pts=Sum('userprofiles__user__activity_user__activity_category__pts')
)

How to use Django annotate?

I'd like to ask, how I could shrink this to one command? I understand that annotate is proper way to do this,but don't understand how.
Here is my code, which is too slow:
sum = 0
for contact in self.contacts.all():
sum += (contact.orders.aggregate(models.Sum('total'))['total__sum'])
return sum
I'd like to get Sum for each contact, all records in total column of relevant orders.
Code above produces sum, but is sluggishly slow. It is my understand it can be done with annotate,but not sure how to use it.
Here is Contact:
class Contact(models.Model):
company = models.ForeignKey(
Company, related_name="contacts", on_delete=models.PROTECT)
first_name = models.CharField(max_length=80)
last_name = models.CharField(max_length=80, blank=True)
email = models.EmailField()
And here is Orders:
class Order(models.Model):
order_number = models.CharField(max_length=80)
company = models.ForeignKey(Company, related_name="orders")
contact = models.ForeignKey(Contact, related_name="orders")
total = models.DecimalField(max_digits=12, decimal_places=6)
order_date = models.DateTimeField(null=True, blank=True)
Help please
You can annotate your queryset on the Contract model with:
from django.db.models import Sum
Contract.objects.annotate(
total_orders=Sum('orders__total')
)
The Contract objects that arise from this queryset will have an extra attribute .total_orders that contains the sum of the total field of the related Order objects.
This will thus create a query that looks like:
SELECT contract.*, SUM(order.total)
FROM contract
LEFT OUTER JOIN order ON order.contract_id = contract.id
GROUP BY contract.id

Complex Query from several models in django

I'm trying to query into three models. I need to get the amount products that are in the stores region location. These are my models:
class Store(models.Model):
name = models.CharField(max_length=255)
logo = models.URLField(null=True, blank=True)
user = models.OneToOneField(get_user_model(), models.CASCADE,
related_name="user_store")
region = models.CharField("Departamento", max_length=100)
city = models.CharField("Ciudad", max_length=100)
class Product(models.Model):
name = models.CharField("Nombre del Producto", max_length=255)
description = models.TextField()
stock = models.PositiveIntegerField()
seller = models.ForeignKey(User, on_delete=models.CASCADE)
Product is related with user and it is related to store, what I want to do is something like this:
{
"Risaralda": 1523,
"Cundinamarca": 8541
}
where keys "Risaralda" and "Cundinamarca" are regions and values are the product amount in these places
I tried something like this
products = Product.objects.filter(
seller__user_store__region__in=Store.objects.filter()
.values("region").distinct())
And I got products in stores regions but i need to count how many products are in every store regions
thank you a lot
Something like this should do the trick:
from django.db.models import F
from django.db.models import Count
Product.objects.annotate(region=F('seller__user_store__region')).annotate(num_products=Count('id')).values_list('region', 'num_products')

django max and groupby together

I have a model which looks like this
player_info, game, score, creation_date
I want to fetch the records with the highest score of each player for a particular game.
Any help is appreciated.
EDIT:
class Game(models.Model):
name = models.CharField(max_length=50)
description = models.TextField(max_length=1000)
logo = models.URLField()
resource_info = models.URLField()
cost = models.DecimalField(default=0.0, decimal_places=2, max_digits=10)
modified_date = models.DateTimeField(auto_now_add=True)
developer_info = models.ForeignKey(User, related_name='uploaded_games', on_delete=models.CASCADE)
class Score(models.Model):
game_info = models.ForeignKey(Game, related_name='game_info',on_delete=models.CASCADE)
player_info = models.ForeignKey(User, related_name='player_info', on_delete=models.CASCADE)
last_played = models.DateTimeField(auto_now=True)
score = models.BigIntegerField(default=0)
If I understand correctly:
You want to filter your Scores by a Game.
And then you want to GROUP BY User and get the MAX score
The GROUP BY part is a bit tricky, but you can achieve it using values + annotate.
Try something like this:
from django.db.models import Max
Score.objects.filter(game_info=...)
.values('player_info').annotate(max=Max('score'))
More info an examples: https://docs.djangoproject.com/en/1.10/topics/db/aggregation/

Calculating score by counting and subtracting foreign key instances in Django

I have a class Summary:
class Summary(models.Model):
title = models.CharField(max_length=128)
category = models.ForeignKey(Category)
subcategory = models.ForeignKey(Subcategory)
content = RichTextField(null=True, blank=True)
users_rated_positive = models.ManyToManyField(
User, blank=True, related_name='summaries_rated_positive')
users_rated_negative = models.ManyToManyField(
User, blank=True, related_name='summaries_rated_negative')
author = models.ForeignKey(User, related_name='summaries_authored')
and a class UserProfile:
class UserProfile(models.Model):
user = models.OneToOneField(User, primary_key=True, related_name='profile')
karma = models.IntegerField(default=0)
rank = models.IntegerField(null=True,blank=True)
I want the karma to be calculated as the positive ratings on all the users summaries minus the negative ratings on all the users summaries
I figured I could add a property like this instead of a field:
#property
def karma(self):
summaries_list = self.user.summaries_authored.all()
positive_karma = sum(
[summary.users_rated_positive.count() for summary in summaries_list])
negative_karma = sum(
[summary.users_rated_negative.count() for summary in summaries_list])
return positive_karma - negative_karma
Is this the right way to do this? I feel like I should be using aggregate, or annotate but to be honest I'm new to Django and not entirely sure how they work for complex situations.
from django.db.models import Count
positive_karma = Summary.objects.filter(author=self.user).aggregate(pos_count=Count('users_rated_positive'))['pos_count']
negative_karma = Summary.objects.filter(author=self.user).aggregate(neg_count=Count('users_rated_negative'))['neg_count']
aggregate returns a dict so the actual value must be retrieved by the key
You would use annotate if you wanted to get the count of pos/neg ratings for every user.