Django aggregation - Sum of two Sums - django

Let's assume we have the following two models:
class Player(Model):
alliance = ForeignKey("Alliance")
points = PositiveIntegerField()
class Alliance(Model):
points = PositiveIntegerField()
Every player and every alliance have a specific amount of points. Total alliance points are count alliance.points + Sum(player_set__points).
What I want to do is to fetch all the alliances ordered by the amount of total points.
The problem is that I do not seem to be able to do Sum + Sum in aggregation.

You can denormalize your DB. Add a field sum on an Alliance, an update it on post_save and post_delete of Alliance and Player. So you'll have ready-to-use value and sort on it easily.

Related

How do I sum a list of attributes from a Google Datastore query result?

Looking for an efficient way to sum a list of attributes using ndb queries. Currently I just fetch the results and run a for loop over them.
class Player(ndb.Model):
score = ndb.IntegerProperty()
score_of_group = 0
all_players = Player.query().filter("various filters").fetch()
for player in all_players:
score_of_group += player.score
If that's the model (a single model) you have and your '''various filters' are just different ways to filter that single model, I don't believe you can sum directly in the queries.
For the Python code itself, you can use the inbuilt sum function and so your code could be something like
all_players = Player.query().filter("various filters").fetch()
score_of_group = sum([x.score for x in all_players])
If you happen to have at least 2 models and you are fetching records from another model based on the value in another model (e.g. you have a list of people and you need to retrieve all their plays/scores from another table), then you should look at #ndb.tasklet to speed up the data retrieval/make it more efficient but you would still have to sum it yourself

How to calculate sum of the difference between two dates

I have this model
class Exemple(models.Model):
from_date = models.DateField()
until_date = models.DateField()
person = models.ForeignKey(Person, on_delete=models.CASCADE)
I have a number per year, exemple 100 and I must to decrease that number from the sum of days of that person. I must to calculate day on every row of that person and then to make sum and then 100 - sum of days
Considering persons contains your persons, you could do something like that :
for person in persons:
sum = 0
for exemple in Exemple.objects.filter(person=person):
sum += max(1, exemple.until_date - exemple.from_date)
Explanation :
1) You do the computation person per person
2) For each person, you browse every exemple
3) You sum every "until - from". The max() is here to return 1 if until_date = from_date is equal to 0 (because you said you don't want it to be 0)
I don't know if you want to store it somewhere or if you just want to do it in a method so I just wrote this little sample of code to provide you the logic. You'll have to adapt it to suit your needs.
However this might not be the prettier way to achieve your goal.

Django ManyToMany Field or something else?

I have a simple eCommerce site with Product and Variation.
Each variation is definitely going to have different weights, and each weight will have a quantity.
I have implemented a Weight model with a ForeignKey relationship to Variation since each variation can have different weights and each weight will have a quantity.
class Weight(models.Model):
variation = models.ForeignKey(Variation)
size = models.DecimalField(decimal_places=3, max_digits=8,
validators=[MinValueValidator(Decimal('0.10'))])
quantity = models.PositiveIntegerField(null=True, blank=True, help_text="Select Quantity for this size")
I added weight as inline and can add multiple weight values and quantity in a variation. Please see this http://imgur.com/XLM6sQJ
One might think this could be possible through creating variation for each weight but since it is definite that each product will have different weights there is no need create a variation for each weight.
Now the problem I am facing is that each variation will have different weights so for e.g. a variation could have weights of 1lb, 2 lb, 3lb. Each of these will create new weight objects for each variation. This means if another variation also has weights of 1lb, 2 lb, 3lb, new objects are created and NOT the existing ones are reused. This will result in a huge db table with many duplicates weight values. This is a problem because there is a limited number of weight and quantity value needed by any product (weight = 1lb to 100lb and quantity = 1 to 100) and so these should ideally be reused.
To avoid this I am thinking to have the Weight model with ManyToMany field to Variation and then quantity should be dropdown for each selected weight. This will allow be to store values of both weight and quantity, and have each product use the same values in each instance.
The problem I have is:
1. Is this the correct approach?
2. if not what is the best approach to do this?
3. If this is the correct approach how to do this?
4. If this is the correct approach how do I display this in admin site since each weight should also have a quantity (I have no clue how to do this)?
5. Is there a better way to achieve this, and if so how?
You have a clear understanding on how you want to do it.
I would agree with you about reusing the same weights for different variations rather than creating new ones which would again have same weights.
This is what I think that would be better there may be multiple ways to do it.
To answer your question, please try this Model relations in your app:
class Quantity(models.Model):
quantity = models.PositiveIntegerField()
class Weight(models.Model):
weight = models.PositiveIntegerField()
quantity = models.ManyToManyField(Quantity)
class Variation(models.Model):
name = models.CharField()
weight = models.ManyToManyField(Weight)
Then add all the weights as you require in the Weights class individually. So after than whenever you need to add some weight to Variation table then you can select the weights from the Weights class in which we have already added the weights that we might require.
In this way you can reuse the same weight for different variations without even having to have duplicate records.
Make sure you have registered both the models in the admin for easy access.
This should solve your problem for having multiple records in the weight table.

How to generate Sum (and other aggregates) in Django where aggregate depends on values from related tables

My model consists of a Portfolio, a Holding, and a Company. Each Portfolio has many Holdings, and each Holding is of a single Company (a Company may be connected to many Holdings).
Portfolio -< Holding >- Company
I'd like the Portfolio query to return the sum of the product of the number of Holdings in the Portfolio, and the value of the Company.
Simplified model:
class Portfolio(model):
some fields
class Company(model):
closing = models.DecimalField(max_digits=10, decimal_places=2)
class Holding(model):
portfolio = models.ForeignKey(Portfolio)
company = models.ForeignKey(Company)
num_shares = models.IntegerField(default=0)
I'd like to be able to query:
Portfolio.objects.some_function()
and have each row annotated with the value of the Portfolio, where the value is equal to the sum of the product of the related Company.closing, and Holding.num_shares. ie something like:
annotate(value=Sum('holding__num_shares * company__closing'))
I'd also like to obtain a summary row, which contains the sum of the values of all of a user's Portfolios, and a count of the number of holdings. ie something like:
aggregate(Sum('holding__num_shares * company__closing'), Count('holding__num_shares'))
I would like to do have a similar summary row for a single Portfolio, which would be the sum of the values of each holding, and a count of the total number of holdings in the portfolio.
I managed to get part of the way there using extra:
return self.extra(
select={
'value': 'select sum(h.num_shares * c.closing) from portfolio_holding h '
'inner join portfolio_company as c on h.company_id = c.id '
'where h.portfolio_id = portfolio_portfolio.id'
}).annotate(Count('holding'))
but this is pretty ugly, and extra seems to be frowned upon, for obvious reasons.
My question is: is there a more Djangoistic way to summarise and annotate queries based on multiple fields, and across related tables?
These two options seem to move in the right direction:
Portfolio.objects.annotate(Sum('holding__company__closing'))
(ie this demonstrates annotation/aggregation over a field in a related table)
Holding.objects.annotate(Sum('id', field='num_shares * id'))
(this demonstrates annotation/aggregation over the product of two fields)
but if I attempt to combine them: eg
Portfolio.objects.annotate(Sum('id', field='holding__company__closing * holding__num_shares'))
I get an error: "No such column 'holding__company__closing'.
So far I've looked at the following related questions, but none of them seem to capture this precise problem:
Annotating django QuerySet with values from related table
Product of two fields annotation
Do I just need to bite the bullet and use raw / extra? I'm hoping that Django ORM will prove the exception to the rule that ORMs really only work as designed for simple queries / models, and anything beyond the most basic ones require either seriously gnarly tap-dancing, or stepping out of the abstraction, which somewhat defeats the purpose...
Thanks in advance!

Update multiple results at once in Django

I want to update budget of Category model in Django.
class Category(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=128)
budget = models.DecimalField(default=0.0, decimal_places=2, max_digits=12, help_text="Amount in dollars ($)")
I would have a list of new budget like this which is equal to number of data in Category:
>>> new_category_budget
[Decimal('2700.000'), Decimal('750.000'), Decimal('1500.000')]
I am updating like this:
>>> for budget in new_category_budget:
... Category.objects.filter(user=2).update(budget=budget)
...
3L
3L
3L
But all of these return the same data. What's wrong with my update statement?
What you're doing is iterating over your budget values, and updating all records with user=2 to each one, overriding the previous value.
The return value of QuerySet.update is the number of updated records. Each time you call update(), you get the result that 3 records were updated.
I don't quite understand what you are actually trying to do, but it might be something like this (untested!):
for (budget, category) in zip(new_category_budget, list(Category.objects.filter(user=2)):
category.budget=budget
category.save()
Of course, this assumes that the number of filtered categories will exactly match the number of budgets in new_category_budget, and also the order of iteration over categories is not obvious. All in all, this seems weird :)
When you call update on a QuerySet, it will set all items in the QuerySet to that value. See the example here.
So through your for loop, you are updating all the Category objects with user=2 to each budget. At the end of the for loop, all the Category objects should be have budget == new_category_budget[-1] or the last budget item.
If you want different values for each Category object, you'll need to call save on them individually.