How do you query #hybrid_property with an aggregate function in SQLAlchemy? - python

here is my sanerio:
i have a model named Order:
i have some fields like that:
#hybrid_property
def final_total(self):
total = self.subtotal
if self.final_discount_amount is not None:
total -= self.final_discount_amount
if self.final_tax_cost is not None:
total += self.final_tax_cost
if self.final_tip is not None:
total += self.final_tip
if self.service_fee is not None:
total += self.service_fee
return total
#final_total.expression
def final_total(cls):
return func.sum(cls.final_total).label("final_total")
i want to get final_total of a record from database using query obj.
here is my query:
session.query(Order.final_total).filter_by(order.id=1).first()
i am getting error like:
python3.8/site-packages/sqlalchemy/ext/hybrid.py", line 1090, in _expr
return ExprComparator(cls, expr(cls), self)
RecursionError: maximum recursion depth exceeded

It is possible to do it using hybrid_property but you can face huge performance problem because this final_total calculation will have to be done during query runtime...
Better option is to add new column final_total to you model (and db) and make this calculation when any of elements (e.g. final_tip, etc.) is changed and the result save in this new column.
However, if hybrid property performance is enough for you, this is implementation that should work:
from sqlalchemy import func
#hybrid_property
def final_total(self):
total = self.subtotal
if self.final_discount_amount is not None:
total -= self.final_discount_amount
if self.final_tax_cost is not None:
total += self.final_tax_cost
if self.final_tip is not None:
total += self.final_tip
if self.service_fee is not None:
total += self.service_fee
return total
#final_total.expression
def final_total(cls):
return (
cls.subtotal +
func.coalesce(cls.final_discount_amount, 0) +
func.coalesce(cls.service_fee, 0) +
func.coalesce(cls.final_tax_cost, 0) +
func.coalesce(cls.final_tip, 0)
)

Related

I got problem a about to call func from class

I got this code from a book name 'Data Structures and Algorithms in Python from Michael T. Goodrich ' chapter 5.5.1 Storing High Scores for a Game.
Can some one can explain to me what this self._board[-1].get_score( ) means ??
I try to print it to see what happens but I got:
print(self._board[-1].get_score( ))
AttributeError: 'NoneType' object has no attribute 'get_score'
class GameEntry:
def __init__(self, name, score):
self._name = name
self._score = score
def get_name(self):
return self._name
def get_score(self):
return self._score
def __str__(self):
return ('({0}, {1})'.format(self._name, self._score)) # e.g., (Bob, 98)
class Scoreboard():
def __init__(self, capacity=10):
self._board = [None]*capacity # reserve space for future scores
self._n=0 # number of actual entries
def __getitem__(self, k):
return self._board[k]
def add(self, entry):
score = entry.get_score()
print(score)
if self._n < len(self._board) or score > self._board[-1].get_score(): # what is it " self._board[-1].get_score()"
if self._n < len(self._board): # no score drops from list
self._n += 1 # so overall number increases
j = self._n - 1
while j > 0 and self._board[j-1].get_score( ) < score:
self._board[j] = self._board[j-1] # shift entry from j-1 to j
j -= 1 # and decrement j
self._board[j] = entry
def __str__(self):
return '\n' .join(str(self._board[j]) for j in range(self._n))
a_ = Scoreboard()
a = ('a','b','c','d')
b = (5,4,8,4)
c = dict(zip(a,b))
print(c)
for i,j in c.items():
x = GameEntry(i,j)
print(x)
y=a_.add(x)
print(y)
Inside your class Scoreboard you keep a list of game entries:
self._board = [None]*capacity # reserve space for future scores
This list is used to keep GameEntry entries:
self._board[j] = entry
The logic in your application uses ._n to track the number of entries added, but only up to total number of score slots available (self._n < len(self._board)).
If this is False (i.e. the number of entries added is the same as the capacity of the entry list when it was initialized), the other part of the or statement gets executed:
score > self._board[-1].get_score()
This looks at the last element in self._board - i.e. what is supposed to be the slot entry with the lowest score to see if this score deserves to be entered into the list instead of the previous one. Any negative list index starts from the end of the list instead of the wrong, so -1 points to the last entry, -2 the second to last entry, and so on.
If you get a None entry while running your code (and not just printing it out before you've added ten entries) is another question, and is probably what you need to debug further (but it doesn't seem to be the case from your code). If you attempt to print that entry before inserting all ten entries (i.e. filling up the entry list), there won't be an entry in last place, and the None placeholder will still be there.

How to calculate total of defs using django?

I would like to calculate the total balance of clients, i did this class with def but it calculate for one client only, i would like to create one def to calculate the total of client_balance of all clients i have.
class ClientsBalance(models.Model):
client = models.OneToOneField(Client, on_delete=models.CASCADE,related_name='Client')
def sales(self):
invoices = self.client.invoice_set.all()
sales_amount = 0
for invoice in invoices:
sales_amount += invoice.amountDuettc()
return sales_amount
def clientpayment(self):
invoices = self.client.invoice_set.all()
clientpayment = 0
for invoice in invoices:
clientpayment += invoice.amount_paid()
return clientpayment
def client_balance(self):
items = self.client.invoice_set.all()
invoice_balance = 0
for item in items:
invoice_balance = (self.sales()) - (self.clientpayment()) + (item.client.initialBalance)
return invoice_balance
First of all, you have a typo in your function which makes each iteration overwriting invoice_balance.
This will return the sum as desired:
def client_balance(self):
items = self.client.invoice_set.all()
invoice_balance = 0
for item in items:
invoice_balance += (self.sales()) - (self.clientpayment()) + (item.client.initialBalance)
return invoice_balance
Second of all you could merge your logic if required since you always use the same queryset like so e.g.:
def calculations(self):
queryset = self.client.invoice_set.all()
sales_amount = 0
clientpayment = 0
invoice_balance = 0
for client in queryset:
sales_amount += client.amountDuettc()
clientpayment += client.amount_paid()
invoice_balance += client.amountDuettc() - client.clientpayment() + client.client.initialBalance()
context = {
'sales_amount': sales_amount,
'clientpayment': clientpayment,
'invoice_balance': invoice_balance
}
return context
You current logic is returning balance of last client. In every iteration, invoice_balance override the previous value.
Try this.
Initialize an empty dictionary and append dictionary in every iteration:
def client_balance(self):
items = self.client.invoice_set.all()
invoice_dict = {}
for item in items:
invoice_balance = (self.sales()) - (self.clientpayment()) + (item.client.initialBalance)
invoice_dict[client_pk] = invoice_balance # client_pk is something unique for client.
return invoice_dict
This will return an dictionary with invoice balance of every client.

trying to applying DRY principle to model methods in django

I have these two methods that are identical except in name and one variable, and it really bugs me, but no matter what I do, I can't figure out how make it so that I just pass a variable into a method in django. These are the two methods, I can post the model if it's needed but I'm fairly sure all the info that is needed is in here, but for clarity, the two model fields are 'launch', 'staff_trials' and 'published' all three are just dates, all other variables are created in the method:
#property
def progress_launch(self):
timeline = self.launch - self.published.date()
current = self.launch - datetime.now().date()
if timeline < current:
percentage == 100
else:
percentage = 100 - round((current/timeline) * 100)
min_bar = 1
max_bar = 100
if percentage is not None:
if percentage < min_bar:
return min_bar
elif percentage > max_bar:
return percentage
else:
percentage = max_bar
return percentage
#property
def progress_trials(self):
timeline = self.staff_trials - self.published.date()
current = self.staff_trials - datetime.now().date()
if timeline < current:
percentage == 100
else:
percentage = 100 - round((current/timeline) * 100)
min_bar = 1
max_bar = 100
if percentage is not None:
if percentage < min_bar:
return min_bar
elif percentage > max_bar:
return percentage
else:
percentage = max_bar
return percentage
I tried to do this:
def progress_launch(self):
return percent(trials)
def progress_trials(self):
return percent(launch)
def percent(_progress)
timeline = _progress - self.published.date()
current = _progress - datetime.now().date()
if timeline < current:
percentage == 100
else:
percentage = 100 - round((current/timeline) * 100)
min_bar = 1
max_bar = 100
if percentage is not None:
if percentage < min_bar:
return min_bar
elif percentage > max_bar:
return percentage
else:
percentage = max_bar
return percentage
But of course it didn't work. The two methods work fine, it just looks terrible and this particular model is getting rather large as it is. I appreciate this is more likely an OOP issue (Which is why I started to learn django in the first place, to learn OOP), as that is where I am struggling still unfortunately. Any help in getting this code refactored in a better way would be very much appreciated. I can't post any of the error messages as there were too many of them, but they were all about variables not being defined.
Just check what model you got using isinstance and use an appropriate field:
#property
def progress(self):
if isinstance(self, LaunchModel):
start = self.launch
else:
start = self.staff_trials
timeline = start - self.published.date()
current = start - datetime.now().date()
# ... etc
You can condense this into a single line:
start = self.launch if isinstance(self, LaunchModel) else self.staff_trials

Is there a way to create a class with an undetermined number of inputs?

I'm trying to create a class for a vector, and as such the number of inputs would depend on the dimension of the vector. Here's my code right now:
class vector:
def __init__(self, entries):
self.elements = []
self.dimensionality = len(entries)
for entry in entries:
self.elements.append(entry)
def __str__(self):
buff = "("
for e in self.elements:
buff += str(e)
if self.elements.index(e) < len(self.elements) - 1:
buff += ", "
buff += ")"
return buff
def __mul__(self, otherVector):
if self.dimensionality != otherVector.dimensionality:
raise RuntimeError("Cannot multiply vectors of different dimensions")
else:
product = 0
for e in self.elements:
product += e * otherVector.elements[self.elements.index(e)]
return product
def __eq__(self, otherVariable):
return size(self) == size(otherVariable)
def size(x):
norm = 0
for e in x.elements:
norm += e**2
return norm**(1/2)
As you can see right now I'm just taking a list as an input so I don't have to deal with that, but I want to do matrices next, and that would require a list of lists, which is a pretty tedious way to input information. Anyone know a way to create a class with a flexible number of arguments?
Thanks

Python greedy thief

Why is it giving me an error " 'int' object is not subscriptable " when i run the program? I looked if i was doing anything wrong, i understand it has to be an integer on line 24, but when I'm changing capacity[1] to capacity(int[1]) , it gives me the same error. Any hint would be appreciated.
class Bag():
__slots__=('name', 'weight', 'value')
def mkBag(name, weight, value):
thisBag = Bag()
thisBag.name = name
thisBag.weight = weight
thisBag.value = value
return thisBag
def ratio(treasure):
print(treasure)
print(treasure)
return treasure[2]//treasure[1]
def plunder(treasure, capacity):
treasure = sorted(treasure, key=ratio, reverse=True)
bagLst = []
current = 0
while current < capacity:
if capacity != 0:
if capacity > current[1]:
bagLst.append(mkBag(treasure[0],weight[1],current[2]))
capacity = capacity - current[1]
else:
bagLst.append(mkBag(current[0], capacity, (current[2]/current[1]), capacity))
capacity = 0
return bagLst
def main():
capacity = 10
name = ''
weight = 0
value = 0
treasure = [('silver', 20, 100), ('platinum', 10, 400), ('paladium',10,800), ('diamonds',5,900), ('gold', 10,60)]
bagLst = plunder(treasure, capacity)
for line in bagLst:
print('bagLst')
current is an int:
current = 0
but you are trying to use it as a list:
if capacity > current[1]:
bagLst.append(mkBag(treasure[0],weight[1],current[2]))
capacity = capacity - current[1]
else:
bagLst.append(mkBag(current[0], capacity, (current[2]/current[1]), capacity))
everywhere you use current[index] you are trying to index the integer value.
If you expected current to be a sequence instead, you'd need to set it to one.
I suspect you want to inspect the current treasure to add to the bag; you didn't pick any treasure item however. Something along the lines of:
current = 0
while capacity and current < len(treasure):
item = treasure[current]
current += 1
if capacity > item[1]:
bagLst.append(mkBag(item[0], item[1], item[2]))
capacity = capacity - item[1]
else:
bagLst.append(mkBag(item[0], capacity, (item[2]/item[1]), capacity))
capacity = 0
"int" object not subscriptable means you're trying to do 1234[1]. That doesn't make any sense! You can subscript a string ('abcdefg'[1] == 'b') and a list ([1,2,3,4,5][1] == 2) but you can't get the "nth element" of an integer.
In your line:
# in def plunder(...):
if capacity > current[1]:
You're trying to access the 2nd element of current, which is currently equal to the integer 0. Are you trying to make that a list? What are you expecting to be in current[1]?
Here's a substantially better way to accomplish this
Hey there, so I figured you meant that current[1] was actually item[1], meaning the weight of the item you were looking at. Instead, current was intended to be the running-weight of the bag. Understood! That said, I wrote up a better solution for this: take a look see!
class Treasure(object):
def __init__(self,name,weight=0,value=0,id_=0):
self.name = name
self.weight = weight
self.value = value
self.id = id_ # bootstrap for further development
#property
def ratio(self):
return self.value/self.weight
class BagFullError(ValueError):
pass
class Bag(object):
def __init__(self,owner=None,capacity=10):
self.owner = owner
self.capacity = capacity
self.contents = list()
def __str__(self):
return_value = "CONTENTS:"
for item in self.contents:
return_value += "\n ${0.value:4} {0.name:10}{0.weight} lb".format(item)
return return_value
def add(self,other):
if not isinstance(other,Treasure):
raise TypeError("Must pick up Treasure")
if self.weight + other.weight > self.capacity:
raise BagFullError("Bag cannot fit {}({} lb) ({} lb/{} lb)".format(
other.name,other.weight,self.weight,self.capacity))
self.contents.append(other)
def remove(self,other):
self.contents.remove(other)
# may throw ValueError if `other` not in `self.contents`
#property
def weight(self):
return sum(item.weight for item in self.contents)
treasure = [Treasure('silver', 20, 100), Treasure('platinum', 10, 400),
Treasure('paladium',10,800), Treasure('diamonds',5,900),
Treasure('gold', 10,60)]
## map(lambda x: Treasure(*x), [('silver',20,100), ... ])
def plunder(treasure_list,bag=None):
_bag = bag or Bag()
treasures = sorted(treasure_list,
key = lambda x: x.ratio,
reverse = True)
while True:
for treasure in treasures:
try: _bag.add(treasure)
except BagFullError as e:
print(e)
return _bag
bag = Bag("Adam",100)
print(bag)
plunder(treasure,bag)
print(bag)
print("Total Value: {}".format(sum(item.value for item in bag.contents)))

Categories

Resources