﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
21040	Bug in db/models/base.py, 'NoneType' object has no attribute 'attname'	kvanman@…	sduveen	"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)
}}}
"	Bug	closed	Database layer (models, ORM)	1.5	Normal	invalid	AttributeError NoneType attname		Unreviewed	1	0	0	0	1	0
