3

I am wondering if it is a good idea to do the following:

I have a Django model (which is related to a migration, therefore it has a database entry) with a bunch of properties. Accessing these are obviously hitting the database every time.

I also have many methods using these properties, like:

def is_good(self):
  return not self.bad and self.good > self.threshold

And these methods are used pretty frequently. So I am trying to reduce the database queries as much as possible, for performance's sake.

Maybe I can take advantage of the fact that my models are very "static", meaning that most of these attributes will never actually change their values, so self.bad or self.good and self.threshold will always hold the same values and it will never change in the database. Perhaps I can use this to my advantage and cache is_good() to reduce the database work?

def is_good(self):
  try:
    return self._is_good
  except AttributeError:
    self._is_good = not self.bad and self.good > self.threshold
  return self._is_good

Is this a common and recommended practice?

1

Django includes a cached_property utility; it turns your method into a property (can be read as if it were an attribute), but after the first time the method is run, it is replaced by its return value so from then on it's just an attribute with that value:

from django.utils.functional import cached_property

@cached_property
def is_good(self):
    return not self.bad and self.good > self.threshold

Now you use it as instance.is_good, without the (). Otherwise it works like what you want except a little faster and without needing the ugly _is_good attribute.

That said, I think your example method will probably run quite quickly already and I doubt this is going to be noticeable.

There are also a lot of other ways to do cacheing in Django, see the documentation for the cacheing framework.

  • Why do you think it runs quickly already? And I knew about @property, I'm guessing @cached_property is the same but with the memoizing? – dabadaba Jan 9 '17 at 12:01
  • I didn't read correctly, I assumed the self.bad and so on were just Django model fields that were retrieved from the database once and then kept around, if they're properties that hit the database each time they're accessed then things are different. Yes @cached_property is similar to a read-only @property. – RemcoGerlich Jan 9 '17 at 12:03
  • Yes, self.bad etc are model fields. But each time self.bad is called it is querying the database right? – dabadaba Jan 9 '17 at 12:10
  • 1
    No, the database is only called once, to create the instance. After that they're just attributes on the object. You need to call .refresh_from_db() explicitly to have the instance refreshed. – RemcoGerlich Jan 9 '17 at 12:14
  • Oh I thought the db was queries when accessing these attributes... So I'm guessing Django has an internal system that refreshed the values of these attributes if they change in the db? And how about relationships? For example self.fruit.name? Or more complex operations on models and QuerySets? How do I know when the db is being hit or not (so I can optimize using cache)? – dabadaba Jan 9 '17 at 12:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.