Opened 5 years ago

Closed 5 years ago

#21040 closed Bug (invalid)

Bug in db/models/, 'NoneType' object has no attribute 'attname'

Reported by: kvanman@… 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


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`)
INSERT INTO test_contact VALUES (1,'aaa','');
INSERT INTO test_contact VALUES (2,'bbb','');
INSERT INTO test_contact VALUES (3,'ccc','');

python shell

import django

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,

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()
[<Contact: aaa ->, <Contact: bbb ->, <Contact: ccc ->]

data = { 'subject': 'aaa' , 'email' : '' }

So far, so good, but now we ran in some problems

f = ContactForm(data)
{'subject': [u'Contact with this Subject already exists.']}

data = { 'subject': 'aaa'}
f = ContactSubjectForm(data)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "./lib/python2.7/site-packages/django/forms/", line 126, in is_valid
    return self.is_bound and not bool(self.errors)
  File "./lib/python2.7/site-packages/django/forms/", line 117, in _get_errors
  File ".l/lib/python2.7/site-packages/django/forms/", line 274, in full_clean
  File "./lib/python2.7/site-packages/django/forms/", line 344, in _post_clean
  File "./lib/python2.7/site-packages/django/forms/", line 353, in validate_unique
  File "./lib/python2.7/site-packages/django/db/models/", line 731, in validate_unique
    errors = self._perform_unique_checks(unique_checks)
  File "./lib/python2.7/site-packages/django/db/models/", 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/", line 466, in _get_pk_val
    return getattr(self,
AttributeError: 'NoneType' object has no attribute 'attname'

The output should be:

{'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/" 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
            # 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 is not None:
                qs = qs.exclude(

Change History (5)

comment:1 Changed 5 years ago by sduveen

Owner: changed from nobody to sduveen
Status: newassigned

comment:2 Changed 5 years ago by sduveen

Resolution: worksforme
Status: assignedclosed

I'm not getting the error. I have a test doing the same thing here:
(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.

comment:3 Changed 5 years ago by kvanman@…

I hope if I understand correctly did you try my example??? Your answer doesn't make sense.

comment:4 Changed 5 years ago by kvanman@…

Resolution: worksforme
Status: closednew

please try my example ...

comment:5 Changed 5 years ago by Koen Biermans <koen@…>

Resolution: invalid
Status: newclosed

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.

Note: See TracTickets for help on using tickets.
Back to Top