If you use Django for your web apps, you will probably have come across a situation where you need to run some custom code on assigning a value to a model field. You’ve probably hacked around this by overriding the save() method or some other arcane hackery, but this is not only unnecessary, but it’s more complicated than it should be.

You can use a Python property to add a getter/setter to the field and run your custom code there. Even better, you don’t even have to change your database or run any migrations to do this.

Since you can’t just create a getter and a setter with the same name as the field (Django will be confused, as it will only see the method and not the shadowed field), you can rename the field. My preferred solution is to prepend the field with an underscore, but still keep the same field column name, to keep everything working as it used to.

So if, for example, you have a date field that you need to run some additional logic on, you can do the following:

If the original model looks like this:

class MyClass(models.Model):
    my_date = models.DateField()

You can add a property like so:

class MyClass(models.Model):
    _my_date = models.DateField(db_column="my_date")

    @property
    def my_date(self):
        return self._my_date

    @my_date.setter
    def my_date(self, value):
        if value > datetime.date.today():
            logger.warning("The date chosen was in the future.")
        self._my_date = value

This way, the entire codebase can stay as-is, without needing to be updated at all. You can even remove the original field and replace it with other functionality, faking it entirely, or remove the properties in the future when you don’t need the custom behavior. It’s perfectly backwards- and forwards-compatible, and very easy to do.