Django querying data out of models and get sum of field - python

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'])

Related

How to append value to the ArrayField in Django with PostgreSQL?

I have created a model with the ArrayField in Django Model. I am using PostgreSQL for the database.
I want to create new rows in the database or update the existing rows.
But I can not insert or append data to the ArrayField.
Model
class AttendanceModel(BaseModel):
"""
Model to store employee's attendance data.
"""
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='attendance_user')
date = models.DateField()
time = ArrayField(models.TimeField(), blank=True, null=True)
class Meta:
unique_together = [['user', 'date']]
def __str__(self):
return f'{self.user.username} - {self.date}'
View
attend, created = models.AttendanceModel.objects.get_or_create(user=fingerprint.user, date=attendance_date)
attend.time = attend.time.append(attendance.timestamp.time())
attend.save()
The time field does not being created or updated with the new value.
How can I do this?
attend.time = attend.time.append(attendance.timestamp.time())
Here the return value of list.append(...) is being assigned to attend.time, which is None, thus the attend.time is not getting updated.
Try this instead,
attend, created = models.AttendanceModel.objects.get_or_create(
user=fingerprint.user, date=attendance_date
)
attend.time.append(attendance.timestamp.time())
attend.save()
Following this Answer I did this.
from django.db.models import Func, F, Value
.
.
.
attend, created = models.AttendanceModel.objects.get_or_create(user=fingerprint.user, date=attendance_date)
attend.time = Func(F('time'), Value(attendance.timestamp.time()), function='array_append')
attend.save()

Django-Tables2 add extra columns from dictionary

I apologize if this question has been asked before but I couldn't find my specific use case answered.
I have a table that displays basic product information. Product details such as price, number of sales, and number of sellers are scraped periodically and stored in a separate database table. Now I want to display both the basic product information and scraped details in one table on the frontend using tables2. To do this, I wrote a function in my Product model to fetch the latest details and return them as a dictionary this way I can use a single Accessor call.
# models.py
class Product(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=256)
brand = models.ForeignKey(Brand)
category = models.CharField(max_length=128, choices=CATEGORY_CHOICES)
def __unicode__(self):
return self.name
def currentState(self):
currentDetailState = ProductDetailsState.objects.filter(
product=self
).latest('created_at')
# return current details as a dictionary
return {
price: currentDetailState.price,
num_sellers: currentDetailState.num_sellers,
num_sales: currentDetailState.num_sales
}
class ProductDetailsState(models.Model):
product = models.ForeignKey(Product)
created_at = models.DateTimeField(auto_now_add=True)
price = models.DecimalField(max_digits=6, decimal_places=2, null=True)
num_sellers = models.IntegerField(null=True)
num_sales = models.IntegerField(null=True)
def __unicode__(self):
return self.created_at
# tables.py
class ProductTable(tables.Table):
productBrand = tables.Column(
accessor=Accessor('brand.name'),
verbose_name='Brand'
)
currentRank = tables.Column(
accessor=Accessor('currentRank')
)
class Meta:
model = Product
...
How do I now use this returned dictionary and split it into columns in my Product table? Is there another way to use an Accessor than how I am doing it?
You can use the Accessor to traverse into the dict, so something like this should work:
class ProductTable(tables.Table):
# brand is the name of the model field, if you use that as the column name,
# and you have the __unicode__ you have now, the __unicode__ will get called,
# so you can get away with jus this:
brand = tables.Column(verbose_name='Brand')
currentRank = tables.Column()
# ordering on the value of a dict key is not possible, so better to disable it.
price = tables.Column(accessor=tables.A('currentState.price'), orderable=False)
num_sellers = tables.Column(accessor=tables.A('currentState.num_sellers'), orderable=False)
num_sales = tables.Column(accessor=tables.A('currentState.num_sales'), orderable=False)
class Meta:
model = Product
While this works, sorting is also nice to have. In order to do that, your 'currentState' method is a bit in the way, you should change the QuerySet you pass to the table. This view shows how that could work:
from django.db.models import F, Max
from django.shortcuts import render
from django_tables2 import RequestConfig
from .models import Product, ProductDetailsState
from .tables import ProductTable
def table(request):
# first, we make a list of the post recent ProductDetailState instances
# for each Product.
# This assumes the id's increase with the values of created_at,
# which probably is a fair assumption in most cases.
# If not, this query should be altered a bit.
current_state_ids = Product.objects.annotate(current_id=Max('productdetailsstate__id')) \
.values_list('current_id', flat=True)
data = Product.objects.filter(productdetailsstate__pk__in=current_state_ids)
# add annotations to make the table definition cleaner.
data = data.annotate(
price=F('productdetailsstate__price'),
num_sellers=F('productdetailsstate__num_sellers'),
num_sales=F('productdetailsstate__num_sales')
)
table = ProductTable(data)
RequestConfig(request).configure(table)
return render(request, 'table.html', {'table': table})
This simplifies the table definition, using the annotations created above:
class ProductTable(tables.Table):
brand = tables.Column(verbose_name='Brand')
currentRank = tables.Column()
price = tables.Column()
num_sellers = tables.Column()
num_sales = tables.Column()
class Meta:
model = Product
You can find the complete working django project at github

How to filter objects by price range in Django?

I have a model Item with field price.
class Item(models.Model):
title = models.CharField(max_length=200, blank='true')
price = models.IntegerField(default=0)
My query may contain min_price & max_price values. So, my request may be like this: http://example.com/api/items?min_price=50&max_price=500. Can anybody tell me, how can I query items between min & max values? Can I solve it using Django ORM?
Thanks!
Check api reference for range. Like it states
You can use range anywhere you can use BETWEEN in SQL — for dates,
numbers and even characters.
So, in your case:
Item.objects.filter(price__range=(min_price, max_price))
Specifying a FilterSet
For more advanced filtering requirements you can specify a FilterSet class that should be used by the view. For example:
import django_filters
from myapp.models import Item
from myapp.serializers import ItemSerializer
from rest_framework import generics
class ItemListFilter(django_filters.FilterSet):
min_price = django_filters.NumberFilter(name="price", lookup_type='gte')
max_price = django_filters.NumberFilter(name="price", lookup_type='lte')
class Meta:
model = Item
fields = ['min_price', 'max_price']
class ItemList(generics.ListAPIView):
queryset = Item.objects.all()
serializer_class = GameSerializer
filter_class = ItemListFilter
Which will allow you to make requests such as:
http://example.com/api/games?min_price=1.20&max_price=8.00

Calculate a number in a Django model from another model's data

I want to take data (amount_spent) from the field of each user and add those numbers up and display them in another field (total_revenue) from a different model (RevenueInfo).
from __future__ import unicode_literals
from django.contrib.auth.models import User
from django.db import models
from django import forms, views
# Create your models here.
#LoginInfo is being used, LoginForms in forms.py is
class LoginInfo(models.Model):
username = models.CharField('', max_length=10)
password = models.CharField('', max_length=15)
class ExtendedProfile(models.Model):
user = models.OneToOneField(User)
amount_spent = models.DecimalField(max_digits=6, decimal_places=2)
class RevenueInfo(models.Model):
total_amount_spent = models.DecimalField(max_digits=6, decimal_places=2, default=0)
total_revenue = models.ForeignKey(ExtendedProfile, null=True)
class Product(models.Model):
category = models.CharField(max_length=100)
name = models.CharField(max_length=100)
description = models.TextField()
#photo = models.ImageField()
price_CAD = models.DecimalField(max_digits=6, decimal_places=2)
quantity = models.DecimalField(max_digits=2, decimal_places=0, null=True)
How could I go about this? Would I iterate of each Usermodel and find User.amount_spent then add that to RevenueInfo.total_revenue? I'm not sure how to put that into code. Also I'm pretty sure I don't need both total_amount_spent and total_revenue but I feel like I need a ForeignKey
You could add a classmethod to the ExtendedProfile model to aggregate the amount_spent value for each User (which bypasses the need for a separate RevenueInfo model):
from django.db.models import Sum
class ExtendedProfile(models.Model):
....
#classmethod
def total_user_spend(cls):
return cls.objects.aggregate(total=Sum('amount_spent'))
Then you can get the total spend using ExtendedProfile.total_user_spend():
>>> ExtendedProfile.total_user_spend()
{'total': Decimal('1234.00')}
Yes, you can write a method for that in your model. There are 2 ways for it.
1) Writing a method that calculates the values and sets it to a instance value.
2) Writing a method that calculates the value and directly returns it.
For example purpose, here is the code for 2nd type.
# models.py
def total_amount_spent(self):
temp_values = [int(user.amount_spent) for user in ExtendedProfile.objects.all()]
return sum(temp_values)
And for using that value in views , but remeber it would be an integer by default
#views.py
value = RevenueInfo.total_amount_spent()
Avoid iterating over database entities in python (it can get really slow). Look into aggregation, it allows you to efficiently get sum (average, max, min, etc...) of values in a database:
>>> from django.db.models import Sum
>>> ExtendedProfile.objects.all().aggregate(Sum('amount_spent'))
{'amount_spent__sum': Decimal('1234.56')}
>>> # ... do whatever you want with the return value

Sum records values in Django

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

Categories

Resources