Errors in Decimal Calcs within def clean method? - django

I'm attempting a few simple calculations in a def clean method following validation (basically spitting out a euro conversion of retrieved uk product price on the fly). I keep getting a TypeError.
Full error reads:
Cannot convert {'product': , 'invoice': , 'order_discount': Decimal("0.00"), 'order_price': {...}, 'order_adjust': None, 'order_value': None, 'DELETE': False, 'id': 92, 'quantity': 8} to Decimal
so I guess django is passing through the entire cleaned_data form to Decimal method. Not sure where I'm going wrong - the code I'm working with is:
def clean_order_price(self):
cleaned_data = self.cleaned_data
data = self.data
order_price = cleaned_data.get("order_price")
if not order_price:
try:
existing_price = ProductCostPrice.objects.get(supplier=data['supplier'], product_id=cleaned_data['product'], is_latest=True)
except ProductCostPrice.DoesNotExist:
existing_price = None
if not existing_price:
raise forms.ValidationError('No match found, please enter new price')
else:
if data['invoice_type'] == 1:
return existing_price.cost_price_gross
elif data['invoice_type'] == 2:
exchange = EuroExchangeRate.objects.latest('exchange_date')
calc = exchange.exchange_rate * float(existing_price.cost_price_gross)
calc = Decimal(str(calc))
return calc
return cleaned_data
If the invoice is of type 2 (a euro invoice) then the system should grab the latest exchange rate and apply that to the matching UK pound price pulled through to get euro result.
Should performing a decimal conversion be a problem within def clean method?
Thanks

I'm going to assume you've made an indentation error on pasting, and the lines from if data['invoice_type'] == 1: should actually be indented one level back - otherwise, as Alex says, the code will never get to the Decimal conversion.
There are multiple other problems with this code, but the biggest is that the last line returns the whole cleaned_data dictionary, rather than the value of this particular field - I suspect this is the cause of the error you are seeing.
Other than that, there is a big error where you calculate calc by multiplying cost_price_gross by exchange. Here exchange is an instance of EuroExchangeRate, rather than a number, so this calculation will not work.

Related

How do i do the summation of two dictionary with aggregate in django , and show it in HTML?

i am new to django , would u experts pls kindly help me..
so i have two modules, and in CBV's get_context_data they both return total sum, now I want to add both the totals and display it on my HTML page, I honestly tried many things but I am always getting an error.
here is my views.py
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
today = datetime.datetime.now()
# Order.objects.filter(created_at__year=today.year, created_at__month=today.month)
context['expense1'] = Tracker.objects.filter(user=self.request.user)
context['Total1'] =(Body.objects.filter(user=self.request.user, pub_date__year=today.year, pub_date__month=today.month).aggregate(total=Sum(F('price') * F('quantity'))),
Sport.objects.filter(user=self.request.user, pub_date__year=today.year, pub_date__month=today.month).aggregate(total=Sum(F('price') * F('quantity'))))
return context
so what I want is , total of body + total of sports , which is being asgn to the context "total1" ,and then I want to show it in HTML
my HTML file
this is how I am displaying the total,
Total: {{Total1}}
Your oneliner code is a bit confusing but I think I get where you made the mistake - the aggregate returns a dict with the key in the argument as the return value. See below:
body_total = Body.objects.filter(
user=self.request.user, pub_date__year=today.year, pub_date__month=today.month
).aggregate(total=Sum(F('price') * F('quantity')))['total']
sport_total = Sport.objects.filter(
user=self.request.user, pub_date__year=today.year, pub_date__month=today.month
).aggregate(total=Sum(F('price') * F('quantity')))['total']
# Note: this is a tuple, not a sum of the two, so check what you need :)
context['Total1'] = (body_total, sport_total)
# For total sum of two totals, just add them of course.
# It seems like this is what you need from the post, and not the
# line above.
context['Total1'] = body_total + sport_total
PS. the code is not tested. I'm a bit unsure if the F expressions work fine (probably yes). Let me know if there are any issues.

Trying to make a captcha field for forms

I'm trying to create a custom captcha field for a form, and it seems to be working alright except for the fact that when the code is supposed to pick a random captcha to return to the end-user for them to solve, it returns a ValueError: too many values to unpack (expected 2). I think this is because the list isn't being randomised and python is selecting the entire list to use as the user's captcha. How can I fix this issue?
class CaptchaField(IntegerField):
widget = CaptchaInput
error_msgs = { 'incorrect': _('Captcha incorrect- try again'), }
def __init__(self):
captcha_array = (
('What is the product of fifteen and four?', 60),
('What is four plus four?', 8),
('What is nine times one?', 9),
('How many letters are in the word orange?', 6),
('What is the sum of ten and two?', 12),
('What is the difference of eighty-four and nineteen?', 65),
('How many letters are in the word forest?', 6),
('How many letter are in the word apple?', 5),
('If there are four palm trees and one dies, how many are alive?', 3),
('What is four divided by two?', 2),
('How many letters are in the name of the capital of France?', 5),
)
captcha = random.choice(captcha_array)
for (a,b) in captcha:
return a
def validate(self, value):
for (a,b) in captcha:
if value == b:
return value
else:
raise ValidationError(self.error_msgs['incorrect'], code = 'incorrect')
captcha = random.choice(captcha_array) returns a tuple (i.e. ('What is the product of fifteen and four?', 60)). If you only want to return a string 'What is the product of fifteen and four?' you can just do return captcha[0].
Otherwise your loop will raise ValueError:
for (a,b) in captcha:
return a
captcha being a tuple will supply only 1 value, not two - first string, then number, to the loop. To unpack values like you do captcha has to be an iterable of tuples (list, dict, ...).
Also, do you expect captcha do be accessible later in validate? If so you need to save captcha in self.

regex year format authentication

I have a program where the user is asked for the session year which needs to be in the form of 20XX-20XX. The constraint here is that it needs to be a year followed by its next year. Eg. 2019-2020.
For example,
Vaild Formats:
2019-2020
2018-2019
2000-2001
Invalid Fromats:
2019-2021
2000-2000
2019-2018
I am trying to validate this input using regular expressions.
My work:
import re
def add_pages(matchObject):
return "{0:0=3d}".format(int(matchObject) + 1)
try:
a = input("Enter Session")
p = r'2([0-9]{3})-2'
p1= re.compile(p)
x=add_pages(p1.findall(a)[0])
p2 = r'2([0-9]{3})-2'+x
p3 = re.compile(p2)
l=p3.findall(a)
if not l:
raise Exception
else:
print("Authenticated")
except Exception as e:
print("Enter session. Eg. 2019-2020")
Question:
So far I have not been able to retrieve a single regex that will validate this input. I did have a look at backreferencing in regex but it only solved half my query. I am looking for ways to improve this authentication process. Is there any single regex statement that will check for this constraint? Let me know if you need any more information.
Do you really need to get the session year in one input?
I think its better to have two inputs (or just automatically set the session year to be the first year + 1).
I don't know if you're aiming for something bigger and this is just an example but using regex just doesn't seem appropriate for this task to me.
For example you could do this:
print("Enter session year")
first_year = int(input("First year: "))
second_year = int(input("Second year: "))
if second_year != (first_year + 1):
# some validation
else:
# program continues
First of all, why regex? Regex is terrible at math. It would be easier to do something like:
def check_years(string):
string = "2011-2012"
years = string.split("-")
return int(years[0]) == (int(years[1]) - 1)

Index Out of Range on Tuple Integer Converted To String

I'm trying to print the last digit of an integer that's been pulled from a database and converted to a string. normally, this code works fine to print the last character of a string even if its a number thats been coverted to a string:
x = 225
print x[-1]
5
However, when I try to do the same thing on a value thats been pulled from a database, the Python interpreter gives me a string index out of range error.
Here is my code:
import MySQLdb
#mysql card_numbercheck
class NumberCheck(object):
def __init__(self):
self.conn = MySQLdb.connect(host='localhost', user='root', passwd='', db='mscan')
self.c = self.conn.cursor()
def query(self, arg, cardname):
self.c.execute(arg, cardname)
return self.c
def __del__(self):
self.conn.close()
# Define SQL statement to select all Data from Column Name
sql = "SELECT card_number FROM inventory_2 WHERE card_name = %s"
#Connect To DB and Get Number of Card.
def Get_MTG_Number():
global card_number
MtgNumber = NumberCheck()
for number in MtgNumber.query(sql, 'Anathemancer'):
card_number = str(number[0])
print card_number[-1]
Get_MTG_Number()
Logically, I don't really understand why this code wouldn't work. Any help would be highly appreciated.
Kind Regards
Jack
Maybe some of the data fields are blank, and by converting to a string you are just getting an empty string? Trying to index such a string will give you an 'index out of range' error.
To see what is going on, you could simply print card_number upon each iteration of the loop. You could also do with a bit more error handling to sanitize the data you are getting from a database.

Search Dictionary function in Python coming up with nothing

So I have a dictionary with these values:
{'AFG': (13, 0, 0, 2), 'ALG': (15, 5, 2, 8), 'ARG': (40, 18, 24, 28)}
And suppose a user wants to find out what the tuple is for the three letter term. Well, he or she would punch in say, 'AFG', and it would output.
[AFG, (13,0,0,2)]
However, it is coming up with the no match found code.
What is going on? I'm punching in the name exactly as it is in the dictionary, and there are no spaces or other white space characters that it is looking for either.
My code:
def findMedals(countryDict, medalDict):
answer = [['Code','country','Gold','Silver','Bronze']]
search_str = input('What is the country you want information on? ')
#this block of code gets the country's name and three letter code onto the list
for code, country in countryDict.items():
if code == search_str:
#This block should be getting executed if I type in, say, AFG
answer = [['country','code'], [country,code]]
print (answer)
else:
#but this is being ran instead
answer = [['country','code'], ['INVALID CODE', 'n/a']]
#return answer
print (medalDict) #debug code
#this block goes and appends the medal count to the list
for code, medalCount in medalDict.items():
if search_str in code:
#this block should be getting executed
answer.append([medalCount])
else:
#it will still put in the AFG's medal count, but with no match founds.
answer.append(['No Match Found'])
print (answer) #debug code
I think it might have something to do with the else statement in the for loop, but bringing it out of the loop doesn't seem to help either.
You are looping over all the keys and values in the dictionary. This means that the else block is going to be called for all keys in that loop that do not match:
for code, country in countryDict.items():
if code == search_str:
#This block should be getting executed if I type in, say, AFG
answer = [['country','code'], [country,code]]
print (answer)
else:
#but this is being ran instead
answer = [['country','code'], ['INVALID CODE', 'n/a']]
#return answer
If the first key-value pair looped over does not match, the else block is executed and you appear to be returning.
To look for a matching key in the dictionary, either test for it with in or use the .get() method to get the value or a default:
if search_key in countryDict:
answer = [['country','code'], [search_key, countryDict[search_key]]
else:
answer = [['country','code'], ['INVALID CODE', 'n/a']]
or use:
country = countryDict.get(search_key)
if country:
answer = [['country','code'], [country, search_key]]
else:
answer = [['country','code'], ['INVALID CODE', 'n/a']]