Django error: [<class 'decimal.InvalidOperation'>] - django

I have done the following signal in my project:
#receiver(pre_save, sender=group1)
#disable_for_loaddata
def total_closing_group1(sender,instance,*args,**kwargs):
total_group_closing_deb_po = instance.master_group.filter(ledgergroups__Closing_balance__gte=0,balance_nature='Debit').aggregate(the_sum=Coalesce(Sum('ledgergroups__Closing_balance'), Value(0)))['the_sum']
total_group_closing_deb_neg = instance.master_group.filter(ledgergroups__Closing_balance__lt=0,balance_nature='Debit').aggregate(the_sum=Coalesce(Sum('ledgergroups__Closing_balance'), Value(0)))['the_sum']
total_group_closing_po_cre = instance.master_group.filter(ledgergroups__Closing_balance__gte=0,balance_nature='Credit').aggregate(the_sum=Coalesce(Sum('ledgergroups__Closing_balance'), Value(0)))['the_sum']
total_group_closing_neg_cre = instance.master_group.filter(ledgergroups__Closing_balance__lt=0,balance_nature='Credit').aggregate(the_sum=Coalesce(Sum('ledgergroups__Closing_balance'), Value(0)))['the_sum']
total_closing_deb_po = instance.ledgergroups.filter(Closing_balance__gte=0,group1_Name__balance_nature='Debit').aggregate(the_sum=Coalesce(Sum('Closing_balance'), Value(0)))['the_sum']
total_closing_deb_ne = instance.ledgergroups.filter(Closing_balance__lt=0,group1_Name__balance_nature='Debit').aggregate(the_sum=Coalesce(Sum('Closing_balance'), Value(0)))['the_sum']
total_closing_cre_po = instance.ledgergroups.filter(Closing_balance__gte=0,group1_Name__balance_nature='Credit').aggregate(the_sum=Coalesce(Sum('Closing_balance'), Value(0)))['the_sum']
total_closing_cre_ne = instance.ledgergroups.filter(Closing_balance__lt=0,group1_Name__balance_nature='Credit').aggregate(the_sum=Coalesce(Sum('Closing_balance'), Value(0)))['the_sum']
if total_group_closing_deb_po != None and total_group_closing_neg_cre != None and total_closing_deb_po != None and total_closing_cre_ne != None:
instance.positive_closing = total_group_closing_deb_po + abs(total_group_closing_neg_cre) + total_closing_deb_po + abs(total_closing_cre_ne)
if total_group_closing_po_cre != None and total_group_closing_deb_neg != None and total_closing_cre_po != None and total_closing_deb_ne != None:
instance.negative_closing = total_group_closing_po_cre + abs(total_group_closing_deb_neg) + total_closing_cre_po + abs(total_closing_deb_ne)
My models are:
class Group1(models.Model):
group_name = models.CharField(max_length=32)
master = models.ForeignKey("self",on_delete=models.CASCADE,related_name='master_group',null=True)
negative_closing = models.DecimalField(max_digits=10,default=0,decimal_places=2,null=True)
positive_closing = models.DecimalField(max_digits=10,default=0,decimal_places=2,null=True)
class Ledger1(models.Model):
name = models.CharField(max_length=32)
group1_name = models.ForeignKey(Group1,on_delete=models.CASCADE,null=True,related_name='ledgergroups')
closing_balance = models.DecimalField(default=0.00,max_digits=10,decimal_places=2,blank=True)
It was working fine in the beginning but all of a sudden when I am increasing the load of the database by putting datas into fields.
It is throwing me the error [<class 'decimal.InvalidOperation'>].
What this error implies?
Any idea anyone
Thank you

I recently got this problem myself. The way I thought I knew about decimal fields was wrong in my case.
I thought max_digits=x gives x values for the integer part and decimal_places=y gives y values for the decimal part but I was wrong.
max_digits define the total no. of digits overall and decimal places are part of the max_digits.
for eg. if max_digits=13 and decimal_places=3 then the field will store numeric values up to a billion with 3 precise decimals.
This may help someone who got stuck as I did for the past few hours.

I had a similar problem and what I have done to solve it is to make sure that the value I want to save fits in the field, two things:
First, use round(myValueFloat, 4). It is, to reduce the number of decimal numbers I try to save.
Use bigger fields for the values I can not reduce.

Related

Django validation of number from query string

Im having this code to create and add students to database.
I need to make validation of count which must be integer, only positive, equal or less 100.
Please help.
def generate_students(request):
count = request.GET.get('count')
studentslist = []
for student in range(0, int(count)):
student = Student.objects.create(first_name = fake.first_name(), last_name = fake.last_name(), age = random.randint(18,100))
studentslist.append(student)
output = ', '.join(
[f"id = {student.id} {student.first_name} {student.last_name}, age = {student.age};" for student in studentslist]
)
return HttpResponse(str(output))
The best way is likely to work with a form, since a form has a lot of validation inplace, can clean the object, and print sensical errors.
We thus can work with a simple form:
from django import forms
class CountForm(forms.Form):
count = forms.IntegerField(min_value=1, max_value=100)
then we can validate the input with:
def generate_students(request):
form = CountForm(request.GET)
if form.is_valid():
count = form.cleaned_data['count']
studentslist = [
Student.objects.create(first_name = fake.first_name(), last_name = fake.last_name(), age = random.randint(18,100))
for _ in range(count)
]
output = ', '.join(
[f'id = {student.id} {student.first_name} {student.last_name}, age = {student.age};'
for student in studentslist]
)
else:
return HttpResponse(str(form.errors))
return HttpResponse(str(output))
Note: Section 9 of the HTTP protocol
specifies that requests like GET and HEAD should not have side-effects, so you
should not change entities with such requests. Normally POST, PUT, PATCH, and
DELETE requests are used for this. In that case you make a small <form> that
will trigger a POST request, or you use some AJAX calls.

Creating a if/else that appends data from mult. scraped pages if counts differ?

I"m trying to scrape Oregon teacher licensure information that looks like this or this(this is publicly available data)
This is my code:
for t in range(0,2): #Refers to txt file with ids
address = 'http://www.tspc.oregon.gov/lookup_application/LDisplay_Individual.asp?id=' + lines2[t]
page = requests.get(address)
tree = html.fromstring(page.text)
count = 0
for license_row in tree.xpath(".//tr[td[1] = 'License Type']/following-sibling::tr[1]"):
license_data = license_row.xpath(".//td/text()")
count = count + 1
if count==1:
ltest1.append(license_data)
if count==2:
ltest2.append(license_data)
if count==3:
ltest3.append(license_data)
with open('teacher_lic.csv', 'wb') as pensionfile:
writer = csv.writer(pensionfile, delimiter="," )
writer.writerow(["Name", "Lic1", "Lic2", "Lic3"])
pen = zip(lname, ltest1, ltest2, ltest3)
for penlist in pen:
writer.writerow(list(penlist))
The problem occurs when this happens: teacher A has 13 licenses and Teacher B has 2. In A my total count = 13 and B = 2. When I get to Teacher B and count equal to 3, I want to say, "if count==3 then ltest3.append(licensure_data) else if count==3 and license_data=='' then license3.append('')" but since there's no count==3 in B there's no way to tell it to append an empty set.
I'd want the output to look like this:
Is there a way to do this? I might be approaching this completely wrong so if someone can point me in another direction, that would be helpful as well.
There's probably a more elegant way to do this but this managed to work pretty well.
I created some blank spaces to fill in when Teacher A has 13 licenses and Teacher B has 2. There were some errors that resulted when the license_row.xpath got to the count==3 in Teacher B. I exploited these errors to create the ltest3.append('').
for t in range(0, 2): #Each txt file contains differing amounts
address = 'http://www.tspc.oregon.gov/lookup_application/LDisplay_Individual.asp?id=' + lines2[t]
page = requests.get(address)
tree = html.fromstring(page.text)
count = 0
test = tree.xpath(".//tr[td[1] = 'License Type']/following-sibling::tr[1]")
difference = 15 - len(test)
for i in range(0, difference):
test.append('')
for license_row in test:
count = count + 1
try:
license_data = license_row.xpath(".//td/text()")
except NameError:
license_data = ''
if license_data=='' and count==1:
ltest1.append('')
if license_data=='' and count==2:
ltest2.append('')
if license_data=='' and count==3:
ltest3.append('')
except AttributeError:
license_data = ''
if count==1 and True:
print "True"
if count==1:
ltest1.append(license_data)
if count==2 and True:
print "True"
if count==2:
ltest2.append(license_data)
if count==3 and True:
print "True"
if count==3:
ltest3.append(license_data)
del license_data
for endorse_row in tree.xpath(".//tr[td = 'Endorsements']/following-sibling::tr"):
endorse_data = endorse_row.xpath(".//td/text()")
lendorse1.append(endorse_data)

django ratings app , negative scoring

models.py
class Restaurant(models.Model)
food_rating = RatingField(range=2, weight=5,can_change_vote = True,allow_delete = True,allow_anonymous = True)
service_rating = RatingField(range=2, weight=5,can_change_vote = True,allow_delete = True,allow_anonymous = True)
ambience_ratiing = RatingField(range=2, weight=5,can_change_vote = True,allow_delete = True,allow_anonymous = True)
view.py code
r = Restaurant.objects.get(pk=1)
r.food_rating.add(score = -1 , user = request.user , ip_address =request.META.get('HTTP_REFERER'))
print r.food_rating.score
error
djangoratings.exceptions.InvalidRating: -1 is not a valid choice for food_rating
doubt
my food_rating field is eligible to take two scores , how am i supposed to change the score so that i can implement vote up and vote down feature , on vote up , i should be able to add 1 to the existing score and on vote down i should be able to subtract a vote , please help , thanks in advance
The problem comes from this script:
if score < 0 or score > self.field.range:
raise InvalidRating("%s is not a valid choice for %s" % (score, self.field.name))
Short answer: convert the [-x:y] interval you want to use for display, into [-x+x:y+x] in your code to avoid this problem. If you wanted [-5:5], then use [-5+5:5+5] which is [0:10]. If you wanted [-50:100] then use [-50+50:100+50] = [0:150] and so on ... It's a simple formula, that shouldn't be a problem for a programer ;)
Long answer: either you fork djangoratings, either you open an issue asking to add a setting enabling negative ratings ... and probably he'll reject it, because of the simple interval conversion workaround, here's some more concrete examples:
class Restaurant(models.Model):
# blabla :)
ambience_rating = RatingField(range=5, weight=5,can_change_vote = True,allow_delete = True,allow_anonymous = True)
def get_adjusted_ambiance_rating(self):
return self.ambience_rating - 3
So, if ambience_rating is "1" (the lowest score), get_adjusted_ambiance_rating() will return -2.
If ambience_rating is "5" (the highest score), get_ambiance_rating_with_negative() will return 2.
Adapt this example/trick to your needs.
You should probably make a single method for all ratings:
def get_adjusted_rating(self, which):
return getattr(self, '%s_rating' % which) - 3
Callable as such:
restaurant.get_adjusted_rating('ambiance')
restaurant.get_adjusted_rating('food')
# etc ...
And maybe a template filter:
#register.filter
def get_adjusted_rating(restaurant, which):
return restaurant.get_adjusted_rating(which)
Usable as such:
{{ restaurant|get_adjusted_rating:"ambiance" }}
{{ restaurant|get_adjusted_rating:"food" }}
{# etc, etc #}
More details about template filters.

quantize result has too many digits for current context

Im trying to save the operation result in my admin.py and i have this error:
quantize result has too many digits for current context
....
def save_model(self, request, obj, form, change):
usuario_libra = obj.consignee.membresia.libra
valores = Valores.objects.get(pk=1)
vtasa = valores.tasa
vaduana = valores.aduana
vgestion = valores.gestion
vfee = valores.fee
vcombustible = valores.combustible
trans_aereo = obj.peso * usuario_libra * vtasa
aduana = (obj.peso * vaduana )*vtasa
fee_airpot = (obj.peso * vfee)*vtasa
combustible = (obj.peso * vcombustible)*vtasa
itbis = (trans_aereo+vgestion)*Decimal(0.16)
total = trans_aereo + vgestion + aduana + fee_airpot + combustible + itbis
if not obj.id:
obj.total = total
...
What this mean?, all my model fields are Decimal
Any help please
Thank you
I was able to solve this problem by increasing the the 'max_digits' field option.
class Myclass(models.Model):
my_field = models.DecimalField(max_digits=11, decimal_places=2, blank=True, null=True)
Be sure to make it large enough to fit the longest number you wish to save.
If that does not work may also need to set the precision larger by:
from decimal import getcontext
...
getcontext().prec = 11
See the python decimal documentation for full context parameters.

How to include "None" in lte/gte comparisons?

I've got this complex filtering mechanism...
d = copy(request.GET)
d.setdefault('sort_by', 'created')
d.setdefault('sort_dir', 'desc')
form = FilterShipmentForm(d)
filter = {
'status': ShipmentStatuses.ACTIVE
}
exclude = {}
if not request.user.is_staff:
filter['user__is_staff'] = False
if request.user.is_authenticated():
exclude['user__blocked_by__blocked'] = request.user
if form.is_valid():
d = form.cleaned_data
if d.get('pickup_city'): filter['pickup_address__city__icontains'] = d['pickup_city']
if d.get('dropoff_city'): filter['dropoff_address__city__icontains'] = d['dropoff_city']
if d.get('pickup_province'): filter['pickup_address__province__exact'] = d['pickup_province']
if d.get('dropoff_province'): filter['dropoff_address__province__exact'] = d['dropoff_province']
if d.get('pickup_country'): filter['pickup_address__country__exact'] = d['pickup_country']
if d.get('dropoff_country'): filter['dropoff_address__country__exact'] = d['dropoff_country']
if d.get('min_price'): filter['target_price__gte'] = d['min_price']
if d.get('max_price'): filter['target_price__lte'] = d['max_price']
if d.get('min_distance'): filter['distance__gte'] = d['min_distance'] * 1000
if d.get('max_distance'): filter['distance__lte'] = d['max_distance'] * 1000
if d.get('available_on'): # <--- RELEVANT BIT HERE ---
filter['pickup_earliest__lte'] = d['available_on'] # basically I want "lte OR none"
filter['pickup_latest__gte'] = d['available_on']
if d.get('shipper'): filter['user__username__iexact'] = d['shipper']
order = ife(d['sort_dir'] == 'desc', '-') + d['sort_by']
shipments = Shipment.objects.filter(**filter).exclude(**exclude).order_by(order) \
.annotate(num_bids=Count('bids'), min_bid=Min('bids__amount'), max_bid=Max('bids__amount'))
And now my client tells me he wants pickup/drop-off dates to be 'flexible' as an option. So I've updated the DB to allow dates to be NULL for this purpose, but now the "available for pickup on" filter won't work as expected. It should include NULL/None dates. Is there an easy fix for this?
Flip the logic and use exclude(). What you really want to do is exclude any data that specifies a date that doesn't fit. If pickup_latest and pickup_earliest are NULL it shouldn't match the exclude query and wont be removed. Eg
exclude['pickup_latest__lt'] = d['available_on']
exclude['pickup_earliest__gt'] = d['available_on']
Most database engines don't like relational comparisons with NULL values. Use <field>__isnull to explicitly check if a value is NULL in the database, but you'll need to use Q objects to OR the conditions together.
Don't think that's actually a django-specific question. Variable 'd' is a python dictionary, no? If so, you can use this:
filter['pickup_latest__gte'] = d.get('available_on', None)