Sum records values in Django - python

I defined couple models in my app:
class Project(models.Model):
title = models.CharField(max_length=150)
url = models.URLField()
manager = models.ForeignKey(User)
class Cost(models.Model):
project = models.ForeignKey(Project)
cost = models.FloatField()
date = models.DateField()
I want to sum costs for each project and render values view.py:
from mypm.costs.models import Project, Cost
from django.shortcuts import render_to_response
from django.db.models import Avg, Sum
def index(request):
# ...
return render_to_response('index.html',...
What is the best way of solving such aggregation in Django ORM?

Alexander's solution will give you the right result, but with one query for each project. Use
annotate to do the whole thing in a single query.
from django.db.models import Sum
annotated_projects = Project.objects.all().annotate(cost_sum=Sum('cost__cost'))
for project in annotated_projects:
print project.title, project.cost_sum

Here's one way to do it. There may be a more efficient way to write the get_total_cost function... but this should work.
Models:
class Project(models.Model):
title = models.CharField(max_length=150)
url = models.URLField()
manager = models.ForeignKey(User)
def get_total_cost(self):
tot = 0
for cost in Cost.objects.filter(project=self):
tot += cost.cost
return tot
class Cost(models.Model):
project = models.ForeignKey(Project)
cost = models.FloatField()
date = models.DateField()
View:
from mypm.costs.models import Project, Cost
from django.shortcuts import render_to_response
from django.db.models import Avg, Sum
def index(request):
return render_to_response('index.html',{'projects',Project.objects.all()},context_instance=RequestContext(request))
Template:
{% for project in projects %}
<p>{{project.title}}: {{project.get_total_cost}}</p>
{% endfor %}
This is pretty basic stuff.
Take some time and go through the Django tutorials and documentation.

Try using aggregate.
from django.db.models import Sum
for project in Projects.objects.all():
print project, project.cost_set.all().aggregate(sum_of_cost=Sum('cost'))['sum_of_cost'] or 0

Related

Django - count number of inline elements in class based view

I have 2 models: Product and ProductComponent. How can I count the number of components in each product so when I want to loop products in the template it will give me the title and the number of components per product?
class ProductComponent(models.Model):
name = models.CharField(max_length=255)
related_product = models.ForeignKey(Product, on_delete=models.CASCADE')
#property
def count_components(self):
return self.component_name.count()
class Product(models.Model):
title = models.CharField(max_length=400)
views.py
class TestView(ListView):
template_name = "test.html"
model = Product
def get_context_data(self, **kwargs):
context = super(TestView, self).get_context_data(**kwargs)
context['product_list'] = Product.objects.filter(?)
return context
I managed to do something like this in admin but only in the ProductComponent admin page but not in the Product admin page.
admin.py
class TestViewAdmin(ImportExportModelAdmin):
resource_class = TestViewAdminResource
list_display = ('get_component_count',)
def get_component_count(self, obj):
return obj.related_product.related_product.count()
What I want to do is to create a loop of products with a number of components that are related to this product.
{% for product in product_list %}
{{product.title}} - {{product.count_components}}
{% endfor %}
I'd appreciate any advice on how to achieve something like that.
you can use the reverse lookup with using related_name on your ForeignKey or if not specified appending '_set' to your related model name:
{{ product.productcomponent_set.count }}
I recommend using related_name parameter on your ForeignKeys though as it makes cleaner and more descriptive code when in some more complicated relationships:
related_product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='components')
then you access it like:
{{ product.components.count }}
More info about backward relationships in Django Docs

Django querying data out of models and get sum of field

I'm just beginning with Django and have the following question:
I have set up a model looking like
class Automation_Apps(models.Model):
app_name = models.CharField(max_length=50)
app_description = models.TextField(blank=True)
view_function_name = models.CharField(max_length=50)
time_saver_min = models.IntegerField()
implementation_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.app_name
class Automation_Usage(models.Model):
used_app = models.ForeignKey(Automation_Apps, on_delete=models.CASCADE)
used_by_user = models.ForeignKey(User, on_delete=models.CASCADE)
used_on_date = models.DateTimeField(auto_now_add=True)
I would like to query it like:
Select Sum(time_saver_min)
from Automation_Apps, Automation_Usage
where Automation_Usage.used_app = Automation_Apps.app_name
The goal of this is the following:
Automation_App will have the Apps and a field, how much time will be saved by using this app
Automation_Usage will have the records, when a user is utilizing the app.
With my query I would like to pull how often the apps where used and how much time was saved by using it (which comes from the Automation_App).
I would like to do this in my view.
To get sum of time utilized over all apps by each user:
from django.db.models import Sum
Automation_Usage.objects.values('used_by_user').annotate(time=Sum('used_app__time_saver_min'))
To get number of users and sum of time utilized for each app:
from django.db.models import Sum, Count
Automation_Usage.objects.values('used_app').annotate(users=Count('used_by_user'), time=Sum('used_app__time_saver_min'))
You can use the Model Sum to Sum the value of your model.
Do it this way;
from django.db.models import Sum
Then
data = Model.objects.aggregate(Sum('time_saver_min'))['time_saver_min__sum'] print(data)
You can use annotate in Django ORM.
My solution:
Sum(time_saver_min) based on Automation_Usage.used_app = Automation_Apps.app_name
=> queryset = Automation_Usage.objects.values('used_app').annotate(Coalesce(Sum('used_app__time_saver_min'), Value(0)))
=> print(queryset['used_app__time_saver_min__sum'])

Displaying the total price of the Items of my Offer in a template

I've got two Models: Offer and Items. My Offer always has an variable amount of Items, which have a price. Now I'd like to display the total sum of the price of the items for each Offer.
How can I do that?
These are my Models:
class Offer(models.Model):
name = models.CharField(default="", verbose_name='Offername', blank=False, max_length=255)
date = models.DateTimeField(default=django_now, verbose_name='Date')
number = models.TextField(default="", verbose_name='Offernumber', blank=True)
class Item(models.Model):
number = models.CharField('Nr.', blank=False, default="", max_length=10)
description = models.TextField(default="", blank=False, verbose_name='Beschreibung')
costs = models.DecimalField('Price', blank=False, default=0, decimal_places=2, max_digits=10)
offer = models.ForeignKey('Offer', null=True, verbose_name="Angebot", related_name="items", on_delete=models.CASCADE)
What I'm trying to do is showing the total sum of all ITEMS that belong to one OFFER. So lets say I've got 2 Offers, each has a DetailView and inside this DetailView I display the Items for every offer. Offer 1 has 3 Items and Offer 2 has 9 Items. Now I want to display for Offer 1 the sum of the 3 items and for Offer 2 the total sum of his 9 items and so on.
Edit: My view
class OfferDetailView(DetailView):
model = Offer
template_name = 'offers/offer_detail.html'
context_object_name = 'offer'
EDIT
You will have to override the DetailView behaviour for this:
class OfferDetailView(DetailView):
model = Offer
template_name = 'offers/offer_detail.html'
context_object_name = 'offer'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
offer = self.object
total_item_cost = sum([i.cost for i in offer.item])
context['total_item_cost'] = total_item_cost
return context
and in the template : {{ total_item_cost }}
(see this part of the doc)
Initial answer
I think the simplest way is to use django annotation to perform the sum at database level. Also see this for your case.
Something like this might work (not tested):
from django.db.models import Sum
offers = Offer.objects.all().annotate(total_costs=Sum("item__costs"))
and in template you can now use:
{% for offer in offers %}
{{ offer.total_costs }}
{% endfor %}
I suppose you can do something like this should work in your case.
from django.db.models import Sum
def get_total_item_offer(self):
total_items = Offer.objects.all()
return self.get_total_item.total_items.all().aggregate(sum=Sum('total')['sum']
Django annotation will work. In your offer model add
def get_total_price(self):
return self.objects.annotate(total_price_sum=Sum("item__costs"))
Each offer model now has a field called total_price_sum which contains the total price of each model associated with it

Updating a Django vote system from an older code. Unorderable types: complex() < complex()

(sorry if there is bad english)
I'm trying to build an upvote system following an old tutorial of Django. The problem is that even if I try to update the code to keep it working it change the functionality. Here is what I have:
stories/models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Story(models.Model):
title = models.CharField(max_length=255)
url = models.URLField()
points = models.IntegerField(default=1)
moderator = models.ForeignKey(User)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
#property
def domain(self):
return urlparse(self.url).netloc
def __str__(self):
return self.title
class Meta:
verbose_name_plural = "stories"
stories/views.py
import datetime
from django.shortcuts import render
from django.utils.timezone import utc
from .models import Story
# Create your views here.
def score(story, gravity=1.8, timebase=120):
points = (story.points - 1)**0.8
now = datetime.datetime.utcnow().replace(tzinfo=utc)
age = int((now - story.created_at).total_seconds())/60
return points/(age+timebase)**1.8
def top_stories(top=180, consider=1000):
latest_stories = Story.objects.all().order_by('-created_at')[:consider]
ranked_stories = sorted([(score(story), story) for story in latest_stories], reverse=True)
return [story for _, story in ranked_stories][:top]
def index(request):
stories = top_stories(top=30)
return render(request, 'stories/index.html', {'stories': stories}
The problem I have is ranked_stories as the way of doing this is old. The new version is using lambda as shown in this answer but it doesn't work.
If I use
ranked_stories = sorted(latest_stories, key=lambda story: story.score(), reverse=True)
it return:
>>'Story' object has no attribute 'score'
Is there a way to make this to work?

Get count for objects

I want to get specialization count for all the doctors and want to show it in the admin panel
Here are the models
class Doctor(models.Model):
name = models.CharField(max_length=1300)
specialization = models.ForeignKey(Specialization)
clinic = models.ForeignKey(Clinic)
class Specialization(models.Model):
name = models.CharField(max_length=30)
For instance, I want to show like:
Dermatologist: 10
Ophthalmologist: 15
I'm very new to Django and have no clue on how to go about it.
Don't use obj.doctor_set.count() recipe. This method will be called for each Specialization record.
Use single SQL query with aggregation:
# admin.py
from django.contrib import admin
from django.db.models import Count
from app.models import Specialization
class SpecializationAdmin(admin.ModelAdmin):
list_display = ['name', 'doctor_count_display']
def get_queryset(self, request):
qs = super(SpecializationAdmin, self).get_queryset(request)
return qs.annotate(doctor_count=Count('doctor')).order_by('-doctor_count')
def doctor_count_display(self, obj):
return obj.doctor_count
doctor_count_display.short_description = 'Number of doctors'
doctor_count_display.admin_order_field = 'doctor_count'
admin.site.register(Specialization, SpecializationAdmin)
you can accomplish this in this way for example:
#admin.py
class SpecializationAdmin(admin.ModelAdmin):
list_display = ('get_count',)
def get_count(self, obj):
return u"{}: {}".format(obj.name, obj.doctor_set.count())
get_count.short_description = "Number of Doctors"
and register this like:
# also in admin.py
admin.site.register(Specialization, SpecializationAdmin)
dont forget to import Specialization model into admin.py
You need to understand about queries Django Queries
To show the number of dermatologist:
Doctor.objects.filter(specialization__name="Dermatologist").count()
To show the number of ophthalmologist
Doctor.objects.filter(specialization__name="Ophthalmologist").count()

Categories

Resources