Reputation:
I'm writing valid inputs into a form so I can save it into my database, however, it will not save because of the following error:
Traceback
C:\Python34\lib\site-packages\django\core\handlers\exception.py in inner response = get_response(request) ...
C:\Python34\lib\site-packages\django\core\handlers\base.py in _get_response response = self.process_exception_by_middleware(e, request)
C:\Python34\lib\site-packages\django\core\handlers\base.py in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs)
G:\NEA Computer Science\mysite\finance\views.py in record_input if form.is_valid(): # check whether it's valid
C:\Python34\lib\site-packages\django\forms\forms.py in is_valid return self.is_bound and not self.errors
C:\Python34\lib\site-packages\django\forms\forms.py in errors self.full_clean()
C:\Python34\lib\site-packages\django\forms\forms.py in full_clean self._post_clean()
C:\Python34\lib\site-packages\django\forms\models.py in _post_clean self.instance.full_clean(exclude=exclude, validate_unique=False)
C:\Python34\lib\site-packages\django\db\models\base.py in full_clean self.clean_fields(exclude=exclude)
C:\Python34\lib\site-packages\django\db\models\base.py in clean_fields setattr(self, f.attname, f.clean(raw_value, self))
C:\Python34\lib\site-packages\django\db\models\fields__init__.py in clean self.run_validators(value)
C:\Python34\lib\site-packages\django\db\models\fields__init__.py in run_validators v(value)
G:\NEA Computer Science\mysite\finance\models.py in validate_pay_method if self.payment_method != "Cash" and self.list_price == self.deposit: AttributeError: 'str' object has no attribute 'payment_method'
I think this is something to do with PAYMENT_CHOICES and the type of value it is, but I cannot think about how to go about it. Thank you!
Code: views.py
from django.http import HttpResponse
from django.shortcuts import render, redirect
from .models import Customer, Dealer
from .forms import CustomerForm
import datetime
# Lists all dealers and links them to their welcome page
def index(request):
dealer_list = Dealer.objects.order_by('name')
html = "<ol>"
for dealer in dealer_list:
html += "<li><a href='"+str(dealer.id)+"/'>"+dealer.name+"</a></li>"
html += "</ol>"
return HttpResponse(html)
# Welcome page for dealer
def welcome(request, dealer_id):
html = "Welcome!<br>"
# Link to results page
html += "<a href='/finance/"+str(dealer_id)+"/results/'>""See your performance""</a><br>"
# Link to input page
html += "<a href='/finance/"+str(dealer_id)+"/record_input/'>""Enter a new record""</a>"
return HttpResponse(html)
# Outputs the results of targets and statistics for a specific dealer
def results(request, dealer_id):
response = """Below are your results:
<input type='button' value='Print' onClick='javascript:window.print()' /><br>""" # Print button
response += "Finance deals made in the last 30 days: <br>"
date_threshold = datetime.date.today() - datetime.timedelta(days=30)
response += str(Customer.objects.filter(date_ordered=date_threshold))
response += "All your customers: "+str(Customer.objects.filter(dealers=dealer_id))+"<br>"
return HttpResponse(response)
# Input form for dealers needing to enter a record into the database
def record_input(request, dealer_id):
if request.method == "POST": # if this is a POST request, process the form data
form = CustomerForm(request.POST) # create a form instance and populate it with data from the request
if form.is_valid(): # check whether it's valid
form += Customer(finance_balance=Customer.validate_balance(), monthly_payment=Customer.validate_month_pay())
form.save() # Save it!
return redirect(success)
# if GET (or any other method), create a blank form
else:
form = CustomerForm()
return render(request, '../templates/finance/form_input.html', {'form': form})
def success(request, dealer_id):
submitted = "SUBMITTED - Thank you!"
return HttpResponse(submitted)
models.py
from django.db import models
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from django.db.models import F
class Dealer(models.Model):
REGION_CHOICES = (
("Northern", "Northern"),
("Central", "Central"),
("Southern", "Southern")
)
def __str__(self):
return self.name
def validate_commission(self):
a = Customer().return_pay_method()
if a != "Cash":
self.commission = F("commission") + 200 # Dealer gains £200 for every financed deal
self.achieved_deals = F("achieved_deals") + 1 # Add 1 to achieved_deals for the dealer
if self.achieved_deals > self.target:
self.commission = F("commission") + 200 # If the target is achieved, gain £400 per deal
return int(self.commission), int(self.achieved_deals)
name = models.CharField(max_length=50)
region = models.CharField(max_length=8, choices=REGION_CHOICES)
target = models.PositiveIntegerField()
achieved_deals = models.PositiveIntegerField()
commission_earned = models.PositiveIntegerField(default=0)
class Product(models.Model):
def __str__(self):
return self.last_order
last_order = models.DateField(auto_now=True)
number_of_orders = models.PositiveIntegerField()
class Login(models.Model):
def __str__(self):
return self.user
def validate_password(self):
if len(self.password) < 8:
raise ValidationError(
_("Password is not long enough."))
dealer = models.OneToOneField(Dealer)
user = models.CharField(max_length=30)
password = models.CharField(max_length=50, validators=[validate_password])
class Customer(models.Model):
New = "New"
Used = "Used"
CONDITION_CHOICES = (
(New, "New"),
(Used, "Used")
)
MANUFACTURER_CHOICES = (
(1, "Citroën"),
(2, "Peugeot"),
(3, "DS")
)
PCP = "PCP" # Personal Contract Purchase
PCH = "PCH" # Personal Contract Hire
HP = "HP" # Hire Purchase
Cash = "Cash"
PAYMENT_CHOICES = (
(PCP, "PCP"),
(PCH, "PCH"),
(HP, "HP"),
(Cash, "Cash")
)
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
def return_pay_method(self):
return str(self.payment_method)
def return_list_price(self):
return int(self.list_price)
def return_deposit(self):
return int(self.deposit)
def validate_price(self): # List price cannot be less than the deposit
a = Customer.return_list_price(self)
b = Customer.return_deposit(self)
if a < b:
raise ValidationError(
_('List price cannot be greater than the deposit.'))
def validate_pay_method(self): # Ensures the correct payment method is used
if self.payment_method != "Cash" and self.list_price == self.deposit:
raise ValidationError(
_('Payment method is not cash.'))
def validate_month_pay(self): # Validates the monthly payment amount
numerator = self.list_price * ((self.interest_rate / 100) / 12) # Formula for monthly payment calculation
denominator = (1 - (1 + (self.interest_rate / 100) / 12)) ** (self.agreement_length * -1)
actual_monthly_payment = round((numerator / denominator), 2) # Rounds correct payments to 2 decimal places
self.monthly_payment = actual_monthly_payment
return self.monthly_payment
def validate_balance(self): # Validates the balance left to pay on finance
self.finance_balance = self.list_price - self.deposit
return int(self.finance_balance)
def validate_interest(self): # Validates to ensure the interest rate isn't negative
if self.interest_rate < 0:
raise ValidationError(
_("Interest rate must be positive."))
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
address_line_1 = models.CharField(max_length=100)
address_line_2 = models.CharField(max_length=100)
town = models.CharField(max_length=60) # Validated up to the longest town name in Britain
postcode = models.CharField(max_length=7)
dealers = models.ManyToManyField(Dealer)
date_ordered = models.DateField(auto_now_add=True)
manufacturers = models.ManyToManyField(Product, max_length=7, choices=MANUFACTURER_CHOICES)
vehicle_model = models.CharField(max_length=50, default="")
vehicle_reg = models.CharField(max_length=7, unique=True)
vehicle_condition = models.CharField(max_length=4, choices=CONDITION_CHOICES)
payment_method = models.CharField(max_length=4, choices=PAYMENT_CHOICES, validators=[validate_pay_method])
list_price = models.PositiveIntegerField(validators=[validate_price])
deposit = models.PositiveIntegerField()
agreement_length = models.PositiveIntegerField()
finance_balance = models.PositiveIntegerField(validators=[validate_balance])
monthly_payment = models.FloatField(validators=[validate_month_pay])
interest_rate = models.FloatField(validators=[validate_interest])
customer_informed = models.BooleanField()
forms.py
from django.forms import ModelForm
from .models import Customer
from django.core.exceptions import ValidationError
class CustomerForm(ModelForm): # The form for dealers to enter their completed sales
class Meta:
model = Customer
fields = ["first_name", "last_name", "address_line_1", "address_line_2", "town", "postcode", "dealers",
"manufacturers", "vehicle_model", "vehicle_reg", "vehicle_condition", "payment_method",
"list_price", "deposit", "agreement_length", "interest_rate", "customer_informed"]
# Hide the balance and monthly payment fields as they are calculated in models.py
Upvotes: 0
Views: 628
Reputation: 194
Something like this should work:
from django import forms
from .models import Customer
from django.core.exceptions import ValidationError
class CustomerForm(forms.ModelForm):
def clean(self):
super(CustomerForm, self).clean()
if self.cleaned_data['payment_method'] != "Cash" and self.cleaned_data['list_price'] == self.cleaned_data['deposit']:
raise forms.ValidationError({'payment_method': _('Payment method is not cash.')})
class Meta:
model = Customer
fields = ["first_name", "last_name", "address_line_1", "address_line_2", "town", "postcode", "dealers",
"manufacturers", "vehicle_model", "vehicle_reg", "vehicle_condition", "payment_method",
"list_price", "deposit", "agreement_length", "interest_rate", "customer_informed"]
Upvotes: 0
Reputation: 194
The problem here is that Django passes the value of the field to a validator - so in this case, self is a string.
You should do this validation in the form instead, by implementing a clean_payment_method() method in your CustomerForm
Upvotes: 0
Reputation: 599550
You can't use an instance method as a validator. It isn't called as a bound method and is only passed the value to validate itself and not the instance, so you can't use it to compare against other values.
Rather than have these separate validator methods, you should have a single clean()
method that does all the validation. You can pass a dictionary of field names to errors to raise multiple validation errors.
Upvotes: 0