Opened 11 years ago
Closed 11 years ago
#21040 closed Bug (invalid)
Bug in db/models/base.py, 'NoneType' object has no attribute 'attname'
Reported by: | Owned by: | sduveen | |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 1.5 |
Severity: | Normal | Keywords: | AttributeError NoneType attname |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | yes | UI/UX: | no |
Description
Got an error:
AttributeError: 'NoneType' object has no attribute 'attname'
Because a model can be 'abstract = True' doesn't have a pk, but a form (also 'abstract = True') can be used for validation 'is_valid()'. I use it for Ajax validation of a single item in a form with multiple items.
See also ticket:17615#comment:6, request to open a new ticket, here it is. 6 month later because I moved to another system and had to install Django again and ran into this bug again. I made a copy of the example etc.
Here is a complete example, starting with a table with some values:
CREATE TABLE test_contact ( `id` int(11) NOT NULL auto_increment, `subject` varchar(64) NOT NULL, `email` varchar(64) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `subject` (`subject`) ) ENGINE=InnoDB; INSERT INTO test_contact VALUES (1,'aaa','aaa@test.com'); INSERT INTO test_contact VALUES (2,'bbb','bbb@test.com'); INSERT INTO test_contact VALUES (3,'ccc','ccc@test.com');
python manage.py shell
import django django.get_version() '1.5' from django.db import models class ContactSubjectManager(models.Manager): def get_query_set(self): return Contact.objects.values('id','subject').all() class ContactSubject(models.Model): subject = models.CharField(max_length=64, unique=True) objects = ContactSubjectManager() class Meta: abstract = True app_label = 'test' class ContactEmailManager(models.Manager): def get_query_set(self): return Contact.objects.values('id','email').all() class ContactEmail(models.Model): email = models.CharField(max_length=64) objects = ContactEmailManager() class Meta: abstract = True app_label = 'test' class Contact(ContactSubject,ContactEmail): objects = models.Manager() class Meta: app_label = 'test' def __unicode__(self): return u'%s - %s' % (self.subject, self.email) from django import forms class ContactSubjectForm(forms.ModelForm): subject = forms.RegexField( regex = r'^[ 0-9a-zA-Z()-]+$', max_length = 30, min_length = 3, error_messages = {'invalid': u'Please enter a valid subject.'}) class Meta: abstract = True model = ContactSubject class ContactEmailForm(forms.ModelForm): email = forms.EmailField( error_messages = {'invalid': u'Please enter a valid email address.'}) class Meta: abstract = True model = ContactEmail class ContactForm(ContactSubjectForm,ContactEmailForm): class Meta: model = Contact f = Contact.objects.all() f [<Contact: aaa - aaa@test.com>, <Contact: bbb - bbb@test.com>, <Contact: ccc - ccc@test.com>] data = { 'subject': 'aaa' , 'email' : 'aaa@test.com' }
So far, so good, but now we ran in some problems
f = ContactForm(data) f.is_valid() False f.errors {'subject': [u'Contact with this Subject already exists.']} data = { 'subject': 'aaa'} f = ContactSubjectForm(data) f.is_valid() Traceback (most recent call last): File "<console>", line 1, in <module> File "./lib/python2.7/site-packages/django/forms/forms.py", line 126, in is_valid return self.is_bound and not bool(self.errors) File "./lib/python2.7/site-packages/django/forms/forms.py", line 117, in _get_errors self.full_clean() File ".l/lib/python2.7/site-packages/django/forms/forms.py", line 274, in full_clean self._post_clean() File "./lib/python2.7/site-packages/django/forms/models.py", line 344, in _post_clean self.validate_unique() File "./lib/python2.7/site-packages/django/forms/models.py", line 353, in validate_unique self.instance.validate_unique(exclude=exclude) File "./lib/python2.7/site-packages/django/db/models/base.py", line 731, in validate_unique errors = self._perform_unique_checks(unique_checks) File "./lib/python2.7/site-packages/django/db/models/base.py", line 823, in _perform_unique_checks model_class_pk = self._get_pk_val(model_class._meta) File "./lib/python2.7/site-packages/django/db/models/base.py", line 466, in _get_pk_val return getattr(self, meta.pk.attname) AttributeError: 'NoneType' object has no attribute 'attname'
The output should be:
f.is_valid() False f.errors {'subject': [u'Contact subject with this Subject already exists.']}
Solution, see also ticket:17615#comment:7
In function _perform_unique_checks "lib/python2.7/site-packages/django/db/models/base.py" remove comment on lines 817-822
# Exclude the current object from the query if we are editing an # instance (as opposed to creating a new one) # Note that we need to use the pk as defined by model_class, not # self.pk. These can be different fields because model inheritance # allows single model to have effectively multiple primary keys. # Refs #17615.
remove code lines:
823 model_class_pk = self._get_pk_val(model_class._meta) 824 if not self._state.adding and model_class_pk is not None: 825 qs = qs.exclude(pk=model_class_pk)
add these 2 lines (same as in Django 1.4)
if not self._state.adding and self.pk is not None: qs = qs.exclude(pk=self.pk)
Change History (5)
comment:1 by , 11 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:2 by , 11 years ago
Resolution: | → worksforme |
---|---|
Status: | assigned → closed |
comment:3 by , 11 years ago
I hope if I understand correctly did you try my example??? Your answer doesn't make sense.
comment:5 by , 11 years ago
Resolution: | → invalid |
---|---|
Status: | new → closed |
The problem is the use of a ModelForm for an abstract model.
In #19271 claudep (core developer) stated that ModelForm is not meant to be used with abstract models. If you think this is wrong and your use case is legitimate, you should bring this up on the django-dev mailinglist.
I'm not getting the error. I have a test doing the same thing here:
https://github.com/schuyler1d/django/commit/cef85acabbdd7970b878f9c8393110757cdc56ee
(which could be pulled just to add a test to the system)
If kvanman can tweak the error to cause the issue, then maybe we can re-open.