Building up subqueries of derived django fields - django

I have a few transformations I need to perform on my table before I aggregate.
I need to multiply transaction_type (which is either 1 or -1) by amount to yield a signed_amount. Then I need to sum all signed_amounts by primary_category (which is a foreign key to secondary category which is a foreign key of my table).
DEBIT = -1
CREDIT = 1
TRANSACTION_TYPE_CHOICES = (
(DEBIT, 'debit'),
(CREDIT, 'credit'),
)
class Transaction(models.Model):
amount = models.DecimalField(max_digits=7, decimal_places=2)
transaction_type = models.IntegerField(choices=TRANSACTION_TYPE_CHOICES)
secondary_category = models.ForeignKey(Secondary_Category)
class Primary_Category(models.Model):
name = models.CharField("Category Name", max_length=30)
category = models.ForeignKey(Primary_Category_Bucket)
class Secondary_Category(models.Model):
name = models.CharField("Category Name", max_length=30)
primary_category = models.ForeignKey(Primary_Category)
I'm stuck on the first bit though.
from django.db.models import Sum, Count, F
original_transactions = Transaction.objects.all()
original_transactions.signed_amount = F('transaction_type') * F('amount')
for transaction in original_transactions:
print transaction.signed_amount
When I try to sanity check that signed_amount is being calculated, I get an error that 'Transaction' object has no attribute 'signed_amount'. I don't want to save signed_amount to the database. I just want to generate it as derived field so I can calculate my totals.
How do I calculate this derived field and subsequently aggregate by primary_category.name?

User python decorator property on a method for class Transaction:
class Transaction(models.Model):
amount = models.DecimalField(max_digits=7, decimal_places=2)
transaction_type = models.IntegerField(choices=TRANSACTION_TYPE_CHOICES)
secondary_category = models.ForeignKey(Secondary_Category)
#property
def signed_amount(self):
return self.amount * self.transaction_type
Then for each Transaction object you can do transaction.signed_amount.
I'm not sure if the aggregation part could be done using queries, but if you don't have that many PrimaryCategory, then python would be good enough to achieve it.

Or you can do this.
all_transactions = Transaction.objects.all().order_by('secondary_category__primary_category_id')
sum = 0
if all_transactions:
primary_category_id = all_transactions[0].secondary_category.primary_category_id
for transaction in all_transactions:
if primary_category_id == transaction.secondary_category.primary_category_id:
sum += (transaction.amount * transaction_type)
else:
sum = (transaction.amount * transaction_type)
print sum

Related

How can I sum up and display a model method field and also display it on the dashboard without looping through the template in Django

How can I sum up and display a model method field and also display it on the dashboard without looping through the template in Django.
I aim to display two values on my dashboard. Total Invested and total Earned. I am able to display total Invested by using the code below.
Formula I used throughout is simple interest = (principal * duration * rate)/100
My view
investments = Investment.objects.all()
total_invested = Investment.objects.aggregate(
total_invested=Sum('amount_deposited '))
context = {
'investments': investments,
'total_invested': total_invested,
}
return render(request, 'list-investments.html', context)```
Then displaying it in my template ```{{total_invested.total_invested}}```
Note: I didnt use **for loop** in my template but I was able to get the total sum invested.
I tried repeating the same procedure to get the total amount earned but Its not working. Any idea how to go about it?
I also want to know how I can save these model methods values in the database.
Model Class
```class Investment(models.Model):
PLAN_CHOICES = (
("Basic - 4% - max 6", "Basic - 4% - max 6"),
("Premium - 5% - max 12", "Premium - 5% - max 12"),
)
plan = models.CharField(max_length=50, choices=PLAN_CHOICES, null=True)
duration = models.IntegerField(max_length=50, null=True)
start_date = models.DateField(null=True)
end_date = models.DateField(null=True)
active_investment = models.BooleanField(default=True)
amount_deposited = models.IntegerField(null=True)
def __str__(self):
return self.plan
def basic_profit(self):
self.basic_pro = (self.amount_deposited*self.duration*4)/100
return self.basic_pro
def premium_profit(self):
self.premium_pro = (self.amount_deposited*self.duration*8)/100
return self.premium_pro
def total_basic_net_profit(self):
self.total_basic_pro = (self.amount_deposited *
self.duration*4)/100 + self.amount_deposited
return self.total_basic_pro
def total_premium_profit(self):
self.total_premium_pro = (
self.amount_deposited *self.duration*8)/100 + self.amount_deposited
return self.total_premium_pro```

Django Update Multiple Object error

i found some problem when i try to update multiple object in my models. here is my models:
class NumberSequence(models.Model):
code = models.CharField(max_length=12)
name = models.CharField(max_length=60)
prefix = models.CharField(max_length=3)
length = models.IntegerField()
last = models.IntegerField(verbose_name='Last Number Used')
def getNumberSequence():
ns = NumberSequence.objects.filter(code='REQ')
letter = ns[0].prefix
lastNumber = ns[0].last+1
l = '{0}-{1:0'+str(ns[0].length)+'d}'
for num in ns:
num.last = lastNumber
num.save()
return l.format(letter,lastNumber+1)
class Requisitions(models.Model):
number = models.CharField(max_length=20, default=getNumberSequence())
transDate = models.DateField(verbose_name='Date')
businessUnit = models.ForeignKey(BusinessUnit, verbose_name='Unit')
division = models.ForeignKey(Division, verbose_name='Division')
remarks = models.TextField
status = models.IntegerField(verbose_name='Status')
when i create new record in Requisition, the table Number Sequence does not update. but if i restart the service, the number sequence table updated automatically.
what's happened with my code?
any suggestion, please..
You should not call the default function in your field definition, but pass the callable only without parentheses.
number = models.CharField(max_length=20, default=getNumberSequence)

Odoo 8 - How to update a Many2one field value?

I'm trying to make module that gives employees visa balance.
What I'm trying to do is that when a visa number is assigned to an employee it changes the counter used_visa to be increased by 1.
so my question is how to update the counter which is in 'visa.balance.line' model when a visa number is selected in 'hr.employee' model
Here's my code:
class hr_visa_balance(models.Model):
_name = "hr.visa.balance"
_rec_name = "visa_no"
visa_no = fields.Char("Visa Number")
approval_date = fields.Date('Approval Date')
visa_line_ids = fields.One2many('visa.balance.line', 'visa_line_id', 'Visa Balance Details')
class visa_balance_line(models.Model):
_name = 'visa.balance.line'
_rec_name = "visa_line_id"
profession = fields.Char()
gender = fields.Selection([('m','Male'),('f','Female')], 'Gender')
country_id = fields.Many2one('res.country', 'Nationality')
available_visa = fields.Integer('Available')
used_visa = fields.Integer('Used')
visa_line_id = fields.Many2one('hr.visa.balance', 'Visa Balance Details')
class hr_employee(models.Model):
_inherit = 'hr.employee',
visa_line = fields.Many2one('visa.balance.line', 'Visa Balance Details')
#api.onchange('visa_line')
def onchange_visa_no(self):
~ code here ~
First of all i am not getting your structure,
i think in employee you have to choose many2one of "hr.visa.balance" object,
and you are choosing "visa.balance.line"
now what you have to do:
give one many2one of hr.visa.balance" in hr.employee and on on_change of visa_no you have to write logic,
Note: you directly can count visa balance in "hr.visa.balance" this object.
no need to take this field in visa.balance.line.

Aggregation based on field in Django

I have a model that has a type and value, based on the type I want to change the sign of the number to negative (Without changing it in the database)
class Foo(models.Model):
EARNING_CATEGORY = 'E'
DEDUCTION_CATEGORY = 'D'
CATEGORY_CHOICES = (
(EARNING_CATEGORY, 'Earning'),
(DEDUCTION_CATEGORY, 'Deduction'),
)
name = models.CharField(max_length=50)
category = models.CharField(max_length=1, choices=CATEGORY_CHOICES)
value = models.FloatField()
Now in order to aggregate over Foo I need to consider that values with category = D should be negative so I get the real sum.
What I tried so far is to change the sign during the save() method, but I don't want to show it to the user as negative in the application. So the only way I came up with is to calculate in a method using a for loop.
class Account(models.Model):
# feilds
def sum(self):
items = self.foo_set.all()
sum = 0
for i in items:
if i.category == Foo.DEDUCTION_CATEGORY:
sum -= i.value
else:
sum += i.value
return sum
You can annotate the current sign during your query like this:
from django.db.models import Case, Value as V, F, FloatField, When
items = self.foo_set.annotate(fixed_value=Case(
When(category=Foo.DEDUCTION_CATEGORY, then=V('-1')*F('value')),
default=F('value'),
output_field=FloatField())
).all()
So during annotation we do a condition check and if our category is equal to 'D', we change sign of value field, and then do your aggregation on fixed_value field.
And here's more info:
https://docs.djangoproject.com/en/1.9/ref/models/conditional-expressions/#case

Django - Add online columns in "select"

I dont know if this is the best way to resolve my problem, if isn't , tell me plz :)
I have this model :
class userTrophy(models.Model):
user = models.ForeignKey(userInfo)
platinum = models.IntegerField()
gold = models.IntegerField()
silver = models.IntegerField()
bronze = models.IntegerField()
level = models.IntegerField()
perc_level = models.IntegerField()
date_update = models.DateField(default=datetime.now, blank=True)
Now i want to retrieve one user info, but i want add 3 new "columns" online :
total = platinum + gold + silver + bronze
point = platinum * 100 + gold * 50 + silver * 25 + bronze * 10
and sort by "point", after sort, put a new column, with a sequencial number: rank (1-n).
Can i do this ( or part of this ) working only with the model ?
I am sure there are many ways to achieve this behavior. The one I am thinking of right now is a Custom Model Manager and transient model fields.
Your class could look like so:
from django.db import models
from datetime import datetime
class UserTrophyManager(models.Manager):
def get_query_set(self):
query_set = super(UserTrophyManager, self).get_query_set()
for ut in query_set:
ut.total = ut.platinum + ut.gold + ut.silver + ut.bronze
ut.points = ut.platinum * 100 + ut.gold * 50 + ut.silver * 25 + ut.bronze * 10
return query_set
class UserTrophy(models.Model):
user = models.CharField(max_length=30)
platinum = models.IntegerField()
gold = models.IntegerField()
silver = models.IntegerField()
bronze = models.IntegerField()
level = models.IntegerField()
perc_level = models.IntegerField()
date_update = models.DateField(default=datetime.now, blank=True)
total = 0
point = 0
objects = UserTrophyManager()
class Meta:
ordering = ['points']
So you can use the following and get total and point calculated:
user_trophies = userTrophy.objects.all()
for user_trophy in user_trophies:
print user_trophy.total
Here's the way I would do it. Add the columns 'total' and 'points' to your model, like this:
class UserTrophy(models.Model):
...
total = models.IntegerField()
points = models.IntegerField()
...
Override the save method for your model:
def save(self, *args, **kwargs):
# Compute the total and points before saving
self.total = self.platinum + self.gold + self.silver + self.bronze
self.points = self.platinum * 100 + self.gold * 50 + \
self.silver * 25 + self.bronze * 10
# Now save the object by calling the super class
super(UserTrophy, self).save(*args, **kwargs)
With total and points as first class citizens on your model, your concept of "rank" becomes just a matter of ordering and slicing the UserTrophy objects.
top_ten = UserTrophy.objects.order_by('-points')[:10]
You'll also want to make sure you have your fields indexed, so your queries are efficient.
If you don't like the idea of putting these fields in your model, you might be able to use the extra feature of Django query set objects to compute your total and points on the fly. I don't use this very often, so maybe someone else can put together an example.
Also, I recommend for you to read PEP 8 for Python coding conventions.
This is more of a followup question than an answer, but is it possible to do something like:
class userTrophy(models.Model):
... stuff...
def points(self):
self.gold + self.silver + self.bronze
then call something like object.points in a template. Im just curious if that is a possibility