quantize result has too many digits for current context - django

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.

Related

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

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.

How do I use data stored in django model in a calculation and then store the result of a calculation in a django model field?

I'm working on my first Django app, and I need to take the data inputted by a user in my models fields, insert it into a function that makes a calculation using that data, and then returns the value of that calculation to my model where it is then stored.
It is not essential that the result be stored in my database, however I will need to use the resulting figure later on to allow the app to determine which data to present to the user.
I have my model class in models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
age = models.IntegerField(default=18)
gender = models.CharField(max_length=6, choices=gend, default='')
weight = models.IntegerField(default=0)
height = models.IntegerField(default=0)
and my function in a services.py file:
def caloriefunction():
weight = Profile.weight
height = Profile.height
age = Profile.age
isMale = Profile.gender
if isMale == "Male":
isMale = True
elif isMale == "Female":
isMale = False
else:
print("Error")
quit()
if isMale:
bmr = 66.5 + (13.75 * weight) + (5 * height) - (6.755 * age)
else:
bmr = 655.1 + (9.6 * weight) + (1.8 * height) - (4.7 * age)
bmr = round(bmr)
return bmr
How would I get the resulting value and then store it in my database or keep it to use in another piece of logic?
Would I be better off using the function in the class as a method?
Sorry if the question isn't being asked very well i'm quite a newbie.
Any help if appreciated!
Yes, you can add that as a method of Profile. And/or if you want to keep the result handy you could add another field to the profile model, and override the save method so it is recalculated whenever an instance is saved. Something like this:
def save(self, *args, **kwargs):
self.update_bmr()
super().save(*args, **kwargs)
def update_bmr(self):
if self.gender == "Male":
self.bmr = (
66.5
+ (13.75 * self.weight)
+ (5 * self.height)
- (6.755 * self.age)
)
elif self.gender == "Female":
self.bmr = (
655.1
+ (9.6 * self.weight)
+ (1.8 * self.height)
- (4.7 * self.age)
)
else:
self.bmr = None
You may need to guard against missing data.

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

Django Custom Model Field with Validation...how to hook it back to ModelForm

A common occurrence I have with one particular project is that it requires the user to enter dimensions (for width/depth/height) in Feet and Inches. Calculations are needed to be performed on that dimension, so I've been working on a custom field type that takes in a dimension in Feet/Inches (eg. 1'-10") and saves it to the database as a decimal number using a regex to parse the input. The field displays to the end-user as feet-inches at all times (with the eventual goal of writing a
method to be able to optionally display in metric, and interact with measure.py, and geodjango stuff). What I have so far is definitely not DRY, but aside from that, I'm having trouble with validation at the form level. The custom model field itself works properly (from what I've seen), and I've written a form field clean method which should work to validate the field. My question is how to hook that form field back into my model form to work for all the width/depth/height fields.
I'm thinking maybe an override of the init on the modelform (a la self.fields['depth']...) , but I'm not quite sure where to go from here...
DCML_PATTERN = re.compile(r'^(?P<feet>\d+)(?P<dec_inch>\.?\d*)\'?$')
FTIN_PATTERN = re.compile(r'^(?P<feet>\d+)\'?\s*-?\s*(?P<inch>[0-9]|10|11)?\"?$')
class FtInField(models.Field):
__metaclass__ = models.SubfieldBase
empty_strings_allowed = False
def db_type(self):
return 'double'
def get_internal_type(self):
return "FtInField"
def to_python(self,value):
if value is u'' or value is None:
return None
if isinstance(value, float):
m = FTDCML_PATTERN.match(str(value))
if m is None:
raise Exception('Must be an integer or decimal number')
feet = int(m.group('feet'))
dec_inch = float(m.group('dec_inch') or 0)
inch = dec_inch * 12
return "%d\'-%.0f\"" % (feet,inch)
return value
def get_db_prep_value(self,value):
if value is u'' or value is None:
return None
m = FTIN_PATTERN.match(value)
if m is None:
raise Exception('Must be in X\'-Y" Format')
feet = int(m.group('feet'))
inch = int(m.group('inch') or 0)
return (feet + (inch/float(12)))
class FtInField(forms.Field):
def clean(self,value):
super(FtInField, self).clean(value)
if value is u'' or value is None:
raise forms.ValidationError('Enter a dimension in X\'-Y" format')
m = FTIN_PATTERN.match(value)
if m is None:
raise forms.ValidationError('Must be in X\'-Y" Format')
feet = int(m.group('feet'))
inch = int(m.group('inch') or 0)
value = '%d\'-%.0f"' % (feet,inch)
return value
class ProductClass(models.Model):
productname = models.CharField('Product Name', max_length=60,blank=True)
depth = FtInField('Depth (Feet/Inches)')
width = FtInField('Width (Feet/Inches)')
height = FtInField('Height (Feet/Inches)')
class ProductClassForm(forms.ModelForm):
depth = FtInField()
width = FtInField()
height = FtInField()
class Meta:
model = ProductClass
class ProductClassAdmin(admin.ModelAdmin):
form = ProductClassForm
Thank you, thank you to both of you. This is what I came up with (based on both of your advise). I'll work on defining a data type to make it better in terms of repetition, but in the meantime, this works...(I was so close, yet so far away...) You guys are amazing. Thanks.
DCML_PATTERN = re.compile(r'^(?P<feet>\d+)(?P<dec_inch>\.?\d*)\'?$')
FTIN_PATTERN = re.compile(r'^(?P<feet>\d+)\'?\s*-?\s*(?P<inch>[0-9]|10|11)?\"?$')
class FtInFormField(forms.Field):
def clean(self,value):
super(FtInFormField, self).clean(value)
if value is u'' or value is None:
raise forms.ValidationError('Enter a dimension in X\'-Y" format')
m = FTIN_PATTERN.match(value)
if m is None:
raise forms.ValidationError('Must be in X\'-Y" Format')
feet = int(m.group('feet'))
inch = int(m.group('inch') or 0)
value = '%d\'-%.0f"' % (feet,inch)
return value
class FtInField(models.Field):
__metaclass__ = models.SubfieldBase
empty_strings_allowed = False
def db_type(self):
return 'double'
def get_internal_type(self):
return "FtInField"
def to_python(self,value):
if value is u'' or value is None:
return None
if isinstance(value, float):
m = FTDCML_PATTERN.match(str(value))
if m is None:
raise Exception('Must be an integer or decimal number')
feet = int(m.group('feet'))
dec_inch = float(m.group('dec_inch') or 0)
inch = dec_inch * 12
return "%d\'-%.0f\"" % (feet,inch)
return value
def get_db_prep_value(self,value):
if value is u'' or value is None:
return None
m = FTIN_PATTERN.match(value)
if m is None:
raise Exception('Must be in X\'-Y" Format')
feet = int(m.group('feet'))
inch = int(m.group('inch') or 0)
return (feet + (inch/float(12)))
def formfield(self, **kwargs):
defaults = {'form_class': FtInFormField}
defaults.update(kwargs)
return super(FtInField,self).formfield(**defaults)
class ProductClass(models.Model):
productname = models.CharField('Product Name', max_length=60,blank=True)
depth = FtInField('Depth (Feet/Inches)')
width = FtInField('Width (Feet/Inches)')
height = FtInField('Height (Feet/Inches)')
class ProductClassForm(forms.ModelForm):
class Meta:
model = ProductClass
class ProductClassAdmin(admin.ModelAdmin):
form = ProductClassForm
To avoid duplication you should probably implement a data type class that handles the parsing of feets and inches for you, it should greatly simplify the other code.
Then you should create a model field and form field, keeping in mind that these are two COMPLETELY SEPARATE components. (which you more or less have already done, but this is just for completeness)
Now, if I'm reading the question right, you want to set the default form field for your model field. To facilitate this you want to implement the formfield() function on your model field class. Ref: the django docs

Custom save method giving invalid tuple size error

I've been stuck on this likely very simple problem, but haven't gotten anywhere with it (newbie to Python and Django). I'm taking some user submitted data and using weights to calculate a score. Despite my best efforts, I'm getting the following when I submit the data via a form: "global name 'appearance' is not defined". I'm pretty sure my issue is in views.py, but I'm not 100% sure. Either a typecast error or just putting the calculation of the score in the wrong place. Any help is much appreciated. Here's my code:
Update: The error I'm receiving after changing my approach to using a custom save method is: "Invalid tuple size in creation of Decimal from list or tuple. The list or tuple should have exactly three elements.".
models.py
# Beer rating weights
APPEARANCE_WEIGHT = 0.15
AROMA_WEIGHT = 0.15
MOUTHFEEL_WEIGHT = 0.10
TASTE_WEIGHT = 0.25
TOTALPACKAGE_WEIGHT = 0.25
SERVING_TYPE = (
('0', 'Choose One'),
('Draft', 'Draft'),
('Bottle', 'Bottle'),
('Can', 'Can'),
)
SCORING = (
(0, ''),
(1, '1'),
(2, '2'),
(3, '3'),
(4, '4'),
(5, '5'),
(6, '6'),
(7, '7'),
(8, '8'),
(9, '9'),
(10, '10'),
)
class Beerrating(models.Model):
beerrated = models.ForeignKey(Beer)
user = models.ForeignKey(User)
date = models.DateTimeField(auto_now_add=True)
servingtype = models.CharField(max_length=10, choices=SERVING_TYPE)
appearance = models.IntegerField(choices=SCORING, default=0)
aroma = models.IntegerField(choices=SCORING, default=0)
mouthfeel = models.IntegerField(choices=SCORING, default=0)
taste = models.IntegerField(choices=SCORING, default=0)
totalpackage = models.IntegerField(choices=SCORING, default=0)
comments = models.TextField()
overallrating = models.DecimalField(max_digits=4, decimal_places=2)
def __unicode__(self):
return u'%s, %s' % (self.user.username, self.beerrated.beername)
def save(self):
if not self.id:
scoredappearance = self.appearance * APPEARANCE_WEIGHT,
scoredaroma = self.aroma * AROMA_WEIGHT,
scoredmouthfeel = self.mouthfeel * MOUTHFEEL_WEIGHT,
scoredtaste = self.taste * TASTE_WEIGHT,
scoredtotalpackage = self.totalpackage * TOTALPACKAGE_WEIGHT,
self.overallrating = (scoredappearance + scoredaroma +
scoredmouthfeel + scoredtaste + scoredtotalpackage)
super(Beerrating, self).save()
forms.py
class BeerReviewForm(ModelForm):
servingtype = forms.CharField(max_length=10,
label=u'Serving Type',
widget=forms.Select(choices=SERVING_TYPE)
)
totalpackage = forms.IntegerField(
label=u'Total Package',
widget=forms.Select(choices=SCORING)
)
class Meta:
model = Beerrating
exclude = ('beerrated', 'user', 'date', 'overallrating')
views.py
def beerreview(request, beer_id):
beer = get_object_or_404(Beer, id=beer_id)
if request.method == 'POST':
form = BeerReviewForm(request.POST)
if form.is_valid():
# Create review
beerrating = Beerrating(
beerrated = beer,
user = request.user,
servingtype = form.cleaned_data['servingtype'],
appearance = form.cleaned_data['appearance'],
scoredappearance = appearance * APPEARANCE_WEIGHT,
aroma = form.cleaned_data['aroma'],
scoredaroma = aroma * AROMA_WEIGHT,
mouthfeel = form.cleaned_data['mouthfeel'],
scoredmouthfeel = mouthfeel * MOUTHFEEL_WEIGHT,
taste = form.cleaned_data['taste'],
scoredtaste = taste * TASTE_WEIGHT,
totalpackage = form.cleaned_data['totalpackage'],
scoredtotalpackage = totalpackage * TOTALPACKAGE_WEIGHT,
comments = form.cleaned_data['comments'],
)
beerrating.save()
return HttpResponseRedirect('/beers/')
else:
form = BeerReviewForm()
variables = RequestContext(request, {
'form': form
})
return render_to_response('beer_review.html', variables)
The error message should specifically tell you the file and line number of the error, but your problem are these two lines in your views.py:
appearance = form.cleaned_data['appearance'],
scoredappearance = appearance * APPEARANCE_WEIGHT,
You are assuming the Python interpreter computes the value for appearance before you use it in the next argument... which is an incorrect assumption.
Define appearance before you create the model instance and your code should then work (or at least break on a different error).
In your save method, the lines:
scoredappearance = self.appearance * APPEARANCE_WEIGHT,
...
are all assigning a tuple, not the number you expect, to the variables. A tuple is basically an immutable list. The training comma on all those lines makes them tuples. What you want is:
scoredappearance = self.appearance * APPEARANCE_WEIGHT
...
Two other problems with your save function. First, because of your indentation, your super only gets called on an update -- which means you'll never be able to create this object!
Secondly, I'd recommend adding the variable arg lists to your save function. This means if it gets called with parameters, they get transparently passed onto the super.
Here's the rewritten function:
def save(self,*args,**kwargs):
if not self.id:
scoredappearance = self.appearance * APPEARANCE_WEIGHT
scoredaroma = self.aroma * AROMA_WEIGHT
scoredmouthfeel = self.mouthfeel * MOUTHFEEL_WEIGHT
scoredtaste = self.taste * TASTE_WEIGHT
scoredtotalpackage = self.totalpackage * TOTALPACKAGE_WEIGHT
self.overallrating = (scoredappearance + scoredaroma +
scoredmouthfeel + scoredtaste + scoredtotalpackage)
super(Beerrating, self).save(*args,**kwargs)
As a final note -- and if you've already done this, I apologize -- I'd really recommending working through a book like Learning Python. Python's a generally straightforward language -- but it has some subtle features (like tuples and variable argument lists) that will cause you trouble if you don't understand them.