Multiplying three models in django and getting the result in views - django

My model:
class VisData(models.Model):
visdata_id = models.AutoField(primary_key=True,blank=True)
user_name = models.ForeignKey(Customer, null=True, on_delete=models.SET_NULL,blank=True)
title = models.CharField(max_length=200, null=True,blank=True)
buy_sell = models.CharField(max_length=1, null=True,blank=True)
date = models.DateField(auto_now_add=False,null=True,editable=True,blank=True)
hour = models.TimeField(auto_now=False, auto_now_add=False,null=True,editable=True,blank=True)
shares_number = models.DecimalField(decimal_places=0,default=0,max_digits=999,null=True,blank=True)
course = models.DecimalField(decimal_places=2,default=0,max_digits=999,null=True,blank=True)
fare = models.DecimalField(decimal_places=2,default=0,max_digits=999,null=True,blank=True)
def __str__(self):
return self.title
I want to assign:
total_value = (shares_number * (course - fare)) and just print it in terminal
My views:
def summaryPage(request):
visdata = VisData.objects.all()
#print(visdata)
context = {}
return render(request, 'smth/homepage.html', context)
I found some close answers but I couldn't understand the solution nor use them in my code.

What you probably need called aggregation:
from django.db.models import F, Sum
def summaryPage(request):
aggregated_data = VisData.objects.annotate(
intermid_result=F('course') - F('fare')
).annotate(
record_total=F('shares_number') * F('intermid_result')
).aggregate(
total=SUM('record_total')
)
result = aggregated_data['total']
print(result)
...
This query will annotate each record with the value of record_total = shares_number * (course - fare) and then calculate a sum for record_total of all records.
Also try to avoid using camelcase function names in Python. See here for details.

Related

Django Query how to sum returned objects method

I have a model:
class TimeStamp(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
t_in = models.DateTimeField(_("In"), auto_now=False, auto_now_add=False)
t_out = models.DateTimeField(
_("Out"), auto_now=False, auto_now_add=False, blank=True, null=True)
class Meta:
verbose_name = _("TimeStamp")
verbose_name_plural = _("TimeStamps")
def __str__(self):
return str(f'{self.t_in.date()} {self.user.get_full_name()}')
def get_absolute_url(self):
return reverse("TimeStamp_detail", kwargs={"pk": self.pk})
def get_total_hrs(self):
if self.t_out:
t_hrs = abs(self.t_out-self.t_in).total_seconds()/3600
else:
t_hrs = '0'
return str(t_hrs)
then in my views.py
def timeclock_view(request):
week_start = timezone.now().date()
week_start -= timedelta(days=(week_start.weekday()+1) % 7)
week_end = week_start + timedelta(days=7)
obj = request.user.timestamp_set.filter(
t_in__gte=week_start, t_in__lt=week_end)
obj.aggregate(total_hrs=Sum('get_total_hrs'))
if obj:
last = obj.last()
context = {'obj': obj, 'last': last,
'week_start': week_start, 'week_end': week_end, 'week_total': total_hrs, }
else:
context = {}
return render(request, 'timeclock/timeclock_view.html', context)
How do i write this to get a sum of the hrs for the queryset?
obj.aggregate(total_hrs=Sum('get_total_hrs)) is giving me an error:
django.core.exceptions.FieldError: Cannot resolve keyword
'get_total_hrs' into field. Choices are: description, id, note,
note_set, pw, t_in, t_out, user, user_id
Aggregation is done on the database. That means you can use only fields in the aggregation, and not model functions or properties.
I would first implement the logic of get_total_hrs with an annotate() queryset, then use the this queryset to aggregate over the calculated field.
from django.db.models.functions import Abs
from django.db.models import F, ExpressionWrapper, DurationField, Sum
queryset.annotate(
total_hrs=ExpressionWrapper(
Abs(F("t_out") - F("t_in")),
output_field=DurationField()
),
).aggregate(overall_hrs=Sum("total_hrs"))

How to perform number of days calculation in Django related field query

I have 2 models:
class Assets(models.Model):
assetMake = models.CharField(max_length=50,blank=True,)
assetModel = models.CharField(max_length=50,blank=True,)
class Maintenance(models.Model):
assetID = models.ForeignKey(Assets, on_delete=models.CASCADE)
dateRequested = models.DateTimeField(null=True, blank=True)
dateCompleted = models.DateTimeField(null=True, blank=True)
complete = models.BooleanField(default = False)
I want to have a view that displays all the Assets in order of when they were last maintained (dateCompleted). Which this accomplishes below:
from django.db.models import Max
Assets.objects.annotate(
last_modified=Max('maintenance__dateCompleted')
).order_by('-last_modified')
But how can I calculate the number of days from dateCompleted to now so that I can display this value as well.
Something like:
Assets.objects.annotate(
last_modified=Max('maintenance__dateCompleted'),
days = ((timezone.now() - maintenance__dateCompleted).days
).order_by('-last_modified')
You can use F() expressions with ExpressionWrapper
from django.db.models import F, ExpressionWrapper
from django.db.models.fields import DurationField
assets_annotate = Assets.objects.annotate(
days_dif = ExpressionWrapper(
timezone.now() - F('maintenance__dateCompleted'),
output_field = DurationField()
)
)
Then you can get days count with assets_annotate[0].days_dif.days

Sub query result in Django not looping from drop down select box

This is the error am getting:
QuerySet.annotate() received non-expression(s): 17
What I want is a subquery that will do something similar to the
select * from inecdb.announced_pu_results
where polling_unit_uniqueid in
(
select uniqueid from polling_unit
where lga_id = (select uniqueid from lga where uniqueid= 17)
);
The subquery
obj3 = Pu_results.objects.filter(polling_unit_uniqueid__in=Subquery(Unit.objects.filter(lga_id=obj1)))
is not displaying any result please can any one help
This is my view
if request.method == 'POST':
selected_item = request.POST.get('item_id') #This is from html select box
obj = Lga.objects.get(lga_id=selected_item)
obj1 = obj.lga_id
obj3 = Pu_results.objects.filter(polling_unit_uniqueid__in=Subquery(Unit.objects.filter(lga_id=obj1)))
for obt in obj3:
print(obt.party_score) #I want looping results here
This is my Model
from django.db import models
from django.urls import reverse
#from django.urls import reverse
class Unit(models.Model):
uniqueid = models.IntegerField(primary_key=True)
polling_unit_id = models.IntegerField(blank=False)
ward_id = models.IntegerField(default=False)
lga_id = models.IntegerField(default=False)
uniquewardid = models.IntegerField(default=True)
polling_unit_number = models.CharField(max_length=50, unique=True)
polling_unit_name = models.CharField(max_length=51)
#pulling_unit_number = models.CharField(max_length=50)
polling_unit_description = models.CharField(max_length=300)
lat = models.CharField(max_length=255)
long = models.CharField(max_length=255)
entered_by_user = models.CharField(max_length=50)
date_entered = models.DateTimeField(blank=False)
user_ip_address = models.CharField(max_length=50)
def get_absolute_url(self):
#return f"/products/{self.id}/"
return reverse("polling:inec-pull-result", kwargs={"uniqueid": self.uniqueid})
class Lga(models.Model):
uniqueid = models.AutoField(primary_key=True)
lga_id = models.IntegerField()
lga_name = models.CharField(max_length=50)
state_id = models.IntegerField()
lga_description = models.TextField(blank=True, null=True)
entered_by_user = models.CharField(max_length=50)
date_entered = models.DateTimeField()
user_ip_address = models.CharField(max_length=50)
class Meta:
db_table = 'lga'
class Article(models.Model):
title = models.CharField(max_length=120)
content = models.TextField()
active = models.BooleanField(default=True)
You need to make sure that the queryset you use for the subquery returns just one column, use values() for that. Note that Subquery isn't needed:
units = Unit.objects.filter(lga_id=obj1).values('uniqueid')
obj3 = Pu_results.objects.filter(polling_unit_uniqueid__in=units)
Read the documentation for the in lookup for a more detailed explanation of passing a QuerySet to in.
This also works:
obj3 = Pu_results.objects.filter(
polling_unit_uniqueid__in = Subquery(
Unit.objects.values('uniqueid').filter(lga_id=obj1)
)
)

django sort on calculated model field -- can I use .extra?

I have the model below. I want to order by percent_vote. I know I could calculate the value and save to a new field in the model, but I prefer not to go that way. Is there some way to use .extra method to do this?
Django 1.6, sqlite3
class HallOfFame(models.Model):
player = models.ForeignKey(Master)
year = models.IntegerField(db_index=True)
voted_by = models.CharField(max_length=30)
ballots_cast = models.IntegerField()
votes_needed = models.IntegerField()
votes_received = models.IntegerField(db_index=True)
inducted = models.BooleanField(db_index=True)
category = models.CharField(max_length=30, db_index=True)
needed_note = models.CharField(max_length=75, blank=True, null=True)
def __unicode__(self):
return "%s %s" % (self.player.name_first, self.player.name_last)
def percent_vote(self):
try:
return self.votes_received/float(self.ballots_cast)
except ZeroDivisionError:
return 0
Yes, it seems you can do something like this, but this may depend on the your database backend ( this should work for PostgreSQL ):
q = HallOfFame.objects.extra(select={'percent_vote_integer': "votes_received/ballots_cast", 'percent_vote_remainder': "votes_received%ballots_cast"})
q = q.extra(order_by = ['percent_vote_integer', percent_vote_remainder])
I ended up solving this issue with the code below. #Emil Davtyan's answer didn't work for me, and I wanted to figure out a more general solution.
def home(request):
hall = HallOfFame.objects.filter(inducted=True, voted_by='BBWAA')
hall_sorted = sorted(hall, key=lambda member: member.percent_vote, reverse=True)[:20]
return render_to_response('top_lists.html', {'hall': hall_sorted })
the model has this:
#property
def percent_vote(self):
try:
return float(self.votes_received)/self.ballots_cast
except ZeroDivisionError:
return 0

correctly aggregating model objects and simple view calculation

if "allotted_pto" (paid time off) is an integer field (expressing number of days) in a UserProfile model:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
fullname = models.CharField(max_length=64, unique=False)
company = models.CharField(max_length=50, choices=CLIENT_CHOICES)
...
allotted_pto = models.IntegerField(max_length=2, blank=True, null=True)
...
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
and "total_days" returns an integer from a vacation request model:
class LeaveRequest(models.Model):
employee = models.ForeignKey(UserProfile)
supervisor = models.ForeignKey(UserProfile, related_name='+', blank=False, null=False)
...
total_days = models.IntegerField(max_length=2, blank=True, null=True)
def __unicode__ (self):
return u'%s %s' % (self.employee, self.submit_date)
def save(self, *args, **kwargs):
fromdate = self.start_date
todate = self.return_date
daygenerator = (fromdate + timedelta(x + 1) for x in xrange((todate - fromdate).days))
self.total_days = sum(1 for day in daygenerator if day.weekday() < 5)
super(LeaveRequest, self).save(*args, **kwargs)
...
how can I construct a view that gives me the sum of "total_days" from a filter set of records and subtract that sum from the "allotted_pto" in the user profile? The simple view I wrote (see below) produces the number of "total_days" objects (in dictionary form) as opposed to counting the actual days, and the request for "allotted_pto" is apparently incorrectly constructed because it returns nothing at all...
#views.py
def leave_screen(request, id):
profile = UserProfile.objects.get(user=id)
records = LeaveRequest.objects.filter(employee=id)
agg_pto = LeaveRequest.objects.aggregate(Count('total_days'))
if profile.allotted_pto: #if the allotted_pto field in UserProfile is not empty
allotted_pto = profile.allotted_pto
remaining_pto = allotted_pto - agg_pto
else:
remaining_pto = "na"
return render_to_response("vacation/leave_request.html", {'records': records, 'agg_pto': agg_pto, 'remaining_pto': remaining_pto})
ok, figured out calculation:
def leave_screen(request, id):
...
agg_pto = LeaveRequest.objects.filter(employee=id).aggregate(Sum('total_days'))
agg_pto = agg_pto['total_days__sum']
just have to figure out how to pull the allotted_pto integer from the User Profile model.
ok, so this wasn't as difficult as I thought. The first challenge was to get an aggregate sum of objects. My first attempt was close but I should have just used "Sum" as opposed to "Count":
agg_pto = LeaveRequest.objects.filter(employee=id).aggregate(Sum('total_days'))
then I just used the python method for extracting the value from a dictionary:
agg_pto = agg_pto['total_days__sum']
finally:
def leave_screen(request, id):
user = request.user.id
profile = request.user.get_profile()
records = LeaveRequest.objects.filter(employee=id).order_by('-submit_date')
agg_pto = LeaveRequest.objects.filter(employee=id).aggregate(Sum('total_days'))
agg_pto = agg_pto['total_days__sum']
allotted_pto = profile.allotted_pto
if allotted_pto: #if the allotted_pto field in UserProfile is not empty
remaining_pto = allotted_pto - agg_pto
else:
remaining_pto = "na"
supervised_records = LeaveRequest.objects.filter(supervisor=id).order_by('-submit_date')
return render_to_response("vacation/leave_request.html", {'records': records, 'supervised_records': supervised_records, 'agg_pto': agg_pto, 'allotted_pto': allotted_pto, 'remaining_pto': remaining_pto, 'profile': profile })
I don't know why it was so hard for me to figure out the syntax for pulling objects from the UserProfile. But I do know that the django-debug-toolbar is very helpful.