'NoneType' object has no attribute 'model'
Question
I have a weird error, when submitting a form. When calling the method form.is_valid() the stack runs down until some of the built-in code tries to access an attribute, that isn't precent... I do not understand where the problem comes from. I simply run
mform = InvAddMainForm(request.POST)
if mform.is_valid():
...
as I've done several times on other forms without trouble. Any idea what could cause this?
EDIT: The form in question looks like this:
class InvAddMainForm(ModelForm):
class Meta:
model = Maintenance
fields = ('serial','end','discount','vat')
def __init__(self, *args, **kwargs):
super(InvAddMainForm, self).__init__(*args, **kwargs)
# The queryset of the field 'serial' is set to None here, as it is
# Derived from the context
self.fields['serial'] = SelectMaintenanceSerialField(required=True, queryset=None)
self.fields['serial'].empty_label = None
self.fields['end'].initial = Option.objects.get(pk='enddate').value
self.fields['vat'].empty_label = None
self.fields['vat'].queryset = Vat.objects.filter(disabled=False).order_by('name')
the relevant parts of the view look like this:
if 'addmain' in request.POST:
mform = InvAddMainForm(request.POST)
print str(mform)
if mform.is_valid():
mf = mform.save(commit=False)
it = mf.serial
if it.disabled:
raise Exception('Cannot buy maintenance to disabled license')
# check if maintenance for given item already is on invoice
if Maintenance.objects.filter(invoice=inv, serial=it):
raise Exception('Item already has maintenance on draft')
startdate = None
# continue maintenance
latest = it.latest_main()
if latest:
startdate = latest.end
# if maintenance date used, add one day
if startdate:
startdate += timedelta(days=1)
if not startdate:
startdate = it.pdate
if not startdate:
itinv = it.invoice()
if itinv:
startdate = itinv.idate
# otherwise use quote date
if not startdate:
ii = ItemInv.objects.filter(invoice=inv, item=it)
if ii:
startdate = inv.qdate
if not startdate:
raise Exception('Could not determine start date')
if startdate > mf.end:
raise Exception("Start date after end date")
# add back maintenance
if startdate < inv.qdate:
back = Maintenance(serial = mf.serial,
invoice = inv,
back = True,
start = startdate,
end = inv.qdate,
price = it.prod.prodprice_set.get(cur=inv.cur).bmprice,
discount = mf.discount,
vat = mf.vat,
parent = latest)
latest = back.save()
startdate = inv.qdate
nm = Maintenance(serial = it,
invoice = inv,
start = startdate,
end = mf.end,
price = it.prod.prodprice_set.get(cur=inv.cur).mprice,
discount = mf.discount,
vat = mf.vat,
parent = latest)
nm.save()
updateinvoice(inv.iid)
return redirect('invoice.invoice.invoice', inv.iid)
else:
mform = InvAddMainForm()
# vat = null AND pdate = null, license imported and removed
mform.fields['serial'].queryset = Item.objects.filter(
account__in=cust_acc
).exclude(
prod__in=Product.objects.filter(prodprice__mprice__exact=0, prodprice__cur=inv.cur)
).exclude(
disabled=True
).exclude(
vat__isnull=True,
pdate__isnull=True
)
mform.fields['discount'].initial = def_discount
mform.fields['vat'].initial = Vat.objects.get(name=calcVAT(inv.customer.main_address.country.cc, inv.customer.vatnr, 'PTS'), disabled=False)
...
The Template simply calls form.as_table()... No magics there...
the traceback looks as follows:
AttributeError at /invoice/10013/
'NoneType' object has no attribute 'model'
Request Method: POST
Request URL: http://127.0.0.1:8000/invoice/10013/
Django Version: 1.3.1
Exception Type: AttributeError
Exception Value:
'NoneType' object has no attribute 'model'
Exception Location: /usr/lib/python2.7/dist-packages/django/forms/models.py in to_python, line 973
Python Executable: /usr/bin/python
Python Version: 2.7.3
Python Path:
['/home/ruben/mnt/derby/v2',
'/usr/lib/python2.7',
'/usr/lib/python2.7/plat-linux2',
'/usr/lib/python2.7/lib-tk',
'/usr/lib/python2.7/lib-old',
'/usr/lib/python2.7/lib-dynload',
'/usr/local/lib/python2.7/dist-packages',
'/usr/lib/python2.7/dist-packages',
'/usr/lib/python2.7/dist-packages/PIL',
'/usr/lib/python2.7/dist-packages/gst-0.10',
'/usr/lib/python2.7/dist-packages/gtk-2.0',
'/usr/lib/python2.7/dist-packages/ubuntu-sso-client',
'/usr/lib/python2.7/dist-packages/ubuntuone-client',
'/usr/lib/python2.7/dist-packages/ubuntuone-control-panel',
'/usr/lib/python2.7/dist-packages/ubuntuone-couch',
'/usr/lib/python2.7/dist-packages/ubuntuone-installer',
'/usr/lib/python2.7/dist-packages/ubuntuone-storage-protocol']
Server time: Wed, 22 Aug 2012 14:13:06 +0200
Traceback Switch to copy-and-paste view
/usr/lib/python2.7/dist-packages/django/core/handlers/base.py in get_response
response = callback(request, *callback_args, **callback_kwargs) ...
▶ Local vars
/usr/lib/python2.7/dist-packages/django/db/transaction.py in inner
res = func(*args, **kwargs) ...
▶ Local vars
/home/ruben/mnt/derby/v2/invoice/invoice.py in invoice
print str(mform) ...
▶ Local vars
/usr/lib/python2.7/dist-packages/django/utils/encoding.py in __str__
return self.__unicode__().encode('utf-8') ...
▶ Local vars
/usr/lib/python2.7/dist-packages/django/forms/forms.py in __unicode__
return self.as_table() ...
▶ Local vars
/usr/lib/python2.7/dist-packages/django/forms/forms.py in as_table
errors_on_separate_row = False) ...
▶ Local vars
/usr/lib/python2.7/dist-packages/django/forms/forms.py in _html_output
top_errors = self.non_field_errors() # Errors that should be displayed above all fields. ...
▶ Local vars
/usr/lib/python2.7/dist-packages/django/forms/forms.py in non_field_errors
return self.errors.get(NON_FIELD_ERRORS, self.error_class()) ...
▶ Local vars
/usr/lib/python2.7/dist-packages/django/forms/forms.py in _get_errors
self.full_clean() ...
▶ Local vars
/usr/lib/python2.7/dist-packages/django/forms/forms.py in full_clean
self._clean_fields() ...
▶ Local vars
/usr/lib/python2.7/dist-packages/django/forms/forms.py in _clean_fields
value = field.clean(value) ...
▶ Local vars
/usr/lib/python2.7/dist-packages/django/forms/fields.py in clean
def clean(self, value):
"""
Validates the given value and returns its "cleaned" value as an
appropriate Python object.
Raises ValidationError for any errors.
"""
value = self.to_python(value) ...
self.validate(value)
self.run_validators(value)
return value
def bound_data(self, data, initial):
"""
▶ Local vars
/usr/lib/python2.7/dist-packages/django/forms/models.py in to_python
def to_python(self, value):
if value in EMPTY_VALUES:
return None
try:
key = self.to_field_name or 'pk'
value = self.queryset.get(**{key: value})
except (ValueError, self.queryset.model.DoesNotExist): ...
raise ValidationError(self.error_messages['invalid_choice'])
return value
def validate(self, value):
return Field.validate(self, value)
▼ Local vars
Variable Value
self
<invoice.forms.SelectMaintenanceSerialField object at 0x7f14c83dc250>
value
u'100035'
key
'pk'
I'm getting confused in the last bit, where the built-in code doesn't work
EDIT: Now, I've found the problem, but not the cause... I use a custom made form field for the form. The field is called SelectMaintenanceSerialField. When i use the default field, everythin works fine. The code for the custom field looks like this:
class SelectMaintenanceSerialField(ModelChoiceField):
'''
When adding maintenance to a product on a draft; formerly,
only the serial of the product was shown. This field allows
for a custom label for that form
'''
def label_from_instance(self, obj):
'''
This method sets the label for each of the objects of the queryset
'''
item = Item.objects.get(pk=str(obj.serial))
name = item.prod.name
return "%s, %s"%(str(obj.serial), str(name))
Either something is wrong with this form, or something needs to be added. The form is displayed as intended, when rendering the template, but apparantly wont work when submitting data
Solution
I'm sorry to have bothered you... I just found the problem... The custom field is made with an empty queryset, since the queryset can only be determined when the view is evaluated. I had forgotten to edit the queryset, when an instance of the form is created from the FORM data. Now it works as intended