0

I have a Django 1.5 app with a custom User model. It has the following field (among a bunch of others):

class User(models.Model):
    ...

    reporting_period = models.CharField(
        max_length=20,
        choices=REPORTING_PERIODS,
        null=True,
        blank=True,
        default=None
    )

    company = models.ForeignKey(
        Company,
        null=True,
        blank=True,
        default=None
    )

This model also has the following function defined:

def get_reporting_period(self, company_reporting_period=None):
    if not self.reporting_period:
        if not hasattr(self, '_company_reporting_period'):
            if company_reporting_period:
                self._company_reporting_period = company_reporting_period
            else:
                self._company_reporting_period = self.company.reporting_period
        return self._company_reporting_period
    else:
        return self.reporting_period

Occasionally, the get_reporting_period function throws an AttributeError ('User' object has no attribute 'reporting_period') on the line if not self.reporting_period:. I am scratching my head as to how that is possible.

self is a User instance, and therefore should have a reporting_period at all times. Are there any times during a model instance's lifetime in Django when a field is not accessible as a property?

Unfortunately, the issue only happens in my production environment where I am unable to debug it.

Krystian Cybulski
  • 10,789
  • 12
  • 67
  • 98
  • Is `User` model is from django's auth module? If not, check your imports, your definition might be getting overridden by built-in `auth.models.User` – Rohan Apr 14 '14 at 09:32
  • If that was the case, then it would error out trying to call the `get_reporting_period`, which exists on my custom `User` model. It was a good suggestion though. Thank you. – Krystian Cybulski Apr 14 '14 at 09:54

1 Answers1

1

Field attributes on instances are not defined until the __init__ method of the Model class is called. Trying to access a field attribute in a custom __init__ method before calling the super method will result in an AttributeError.

Otherwise something must've accidentally deleted the attribute on the instance.

I believe that are the only two instances when a field attribute is not defined on a model instance.

EDIT: I've done a small test, and unpickling preserves the attributes of the original version of the model, but new methods carry over to the recreated model. So you would be able to call the new get_reporting_period function, but it would raise an AttributeError when trying to access the undefined reporting_period attribute.

That means that in this case you should invalidate your cached entries after adding a new field. However, there are plenty of serialization methods that would not result in such an error. Take a look at the answer to this question for a way to serialize models without this problem.

Community
  • 1
  • 1
knbk
  • 52,111
  • 9
  • 124
  • 122
  • Here is an idea... Could you help me validate it? I have recently added the `reporting_period` field and the `get_reporting_period` function to my model and have done a deployment. In certain cases I cache the `User` object in memcached. In the code path which is exhibiting this issue is likely to have retrieved the `User` instance from the cache. Could it be possible that an old version of the `User` object is being retrieved form the cache? The pre-deployment version would not have a `reporting_period` field. Does unpickling work like this, where the `get_reporting_period` would exist? – Krystian Cybulski Apr 14 '14 at 10:07
  • Ah yes, that was most likely your problem. You should invalidate your cache and/or use another method for model serialization. – knbk Apr 14 '14 at 10:35