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)
In your save method, the lines:
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:
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:
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.