Lutz Prechelt
Lutz Prechelt

Reputation: 39286

AttributeError: 'NoneType' object has no attribute 'attname' (Django)

I have a fairly complex model for which the first call to MyModel.objects.create(**kwargs) fails with

AttributeError: 'NoneType' object has no attribute 'attname'

The stack trace dives down like this (in Django 1.11)

django/db/models/manager.py:85: in manager_method
   return getattr(self.get_queryset(), name)(*args, **kwargs)
django/db/models/query.py:394: in create
   obj.save(force_insert=True, using=self.db)
django/db/models/base.py:807: in save
   force_update=force_update, update_fields=update_fields)
django/db/models/base.py:837: in save_base
   updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
django/db/models/base.py:889: in _save_table
   pk_val = self._get_pk_val(meta)
django/db/models/base.py:644: in _get_pk_val
   return getattr(self, meta.pk.attname)
django/db/models/query_utils.py:114: in __get__
   val = self._check_parent_chain(instance, self.field_name)
django/db/models/query_utils.py:131: in __check_parent_chain
   return getattr(instance, link_field.attname)

The model definition looks alright to me. I have checked all the parameters of the create call are just what I want them to be.

I'm not keen on stripping down the model to find the problem, because the model is so complex. (All my other models, many of them similar, appear to work fine.)

So what might cause this strange message?

Upvotes: 20

Views: 14004

Answers (6)

ndpu
ndpu

Reputation: 22561

This error can occur with django proxy model inheritance. For example, let assume that app have normal django model Report and proxy model ReportExist inherited from it. Error occurs when another normal model (ReportSelect) is inherited from proxy model:

class Report(models.Model):
   # normal model

class ReportExist(Report):
    class Meta:
        proxy = True

class ReportSelect(ReportExist):
   # no meta, error

Adding a Meta class with proxy = True to the ReportSelect resolves the error:

class ReportSelect(ReportExist):
   class Meta:
       proxy = True

   # no error

Upvotes: 1

Marko Andrijevic
Marko Andrijevic

Reputation: 39

This is more a comment than a solution but I don't have enough reputation to comment so need to put it like this. In our case, the problem was that we used Python f-strings in logger calls, which contained Django model objects as vars. The problem was 100% reproducible. After replacing f-stings with recommended string formatting style for Python lazy logger (e.g. logger.info('Hello %s', name) instead of logger.info(f'Hello {name}')) the issue was gone. I hope I'll save someone from hours of t-shooting.

Upvotes: 0

David
David

Reputation: 502

to reformulate the previous and make it more explicit: this error is due to an __init__ method that is not referencing its parent class. make sure your __init__ method is defined like this:

def __init__(self, *args, **kwargs):
    super().__init__(*args,**kwargs)
    ...

Upvotes: 2

Jafeth Yahuma
Jafeth Yahuma

Reputation: 91

As for me, I had:

 def __init__(self):
        return self.title

instead of

 def __str__(self):
        return self.title

so I had mistyped __init__ in place of __str__

Upvotes: 7

user5359531
user5359531

Reputation: 3555

I ran into this error, but from a completely different use case. I ended up breaking my Django model instance object by removing the id attribute from its __dict__ representation, then later trying to access the id attribute again as shown below. Solution: do not remove items from your Django model instance's __dict__ if you plan to use those attributes later on the original instance object.

instance1 = File.objects.get(path = '/path/to/sample1.txt')

print(instance1)
# /path/to/sample1.txt # this is my normal __str__ method for the model

# preview the instance dict representation
print(instance1.__dict__)
# {'_state': <django.db.models.base.ModelState object>, 'id': 7, 'path': '/path/to/sample1.txt'}

# create object to hold the dict
instance1_dict = instance1.__dict__

# remove 'id' for test case because its non-deterministic
instance1_dict.pop('id')

# check what the dict object looks like now
print(instance1_dict)
# {'_state': <django.db.models.base.ModelState object>, 'path': '/path/to/sample1.txt'}
# 'id' no longer present

# check what the original instance object dict representation looks like
print(instance1.__dict__)
# {'_state': <django.db.models.base.ModelState object>, 'path': '/path/to/sample1.txt'}
# 'id' is no longer present anymore

# try to access 'id' on the original instance object
expected_id = instance1.id
# AttributeError: 'NoneType' object has no attribute 'attname'

Upvotes: 0

Lutz Prechelt
Lutz Prechelt

Reputation: 39286

It took about 90 minutes to find this. I only found it after I had taken out of my model first the abstract supermodel, then all relationship fields, then all-but-one data fields, until only a single IntegerField was left. The create still didn't work.

At that point I called create on some other simple model class MyModel2 in exactly the same test context. It worked (like the idiomatic breeze).

So what the hell was special about MyModel??

And then it dawned on me: MyModel had an __init__ method; most of my other models did not. So look at that. And bang your forehead: I simply had forgotten the mandatory (in Python 3 style)

super().__init__(*args, **kwargs)

Moral: Don't forget this or you may suffer from a really tough error message.

(Note: If you don't like the style of this post, I am sorry. It was required therapeutic writing for me.)

Upvotes: 40

Related Questions