Do properties work on Django model fields? Do properties work on Django model fields? python python

Do properties work on Django model fields?


A model field is already property, so I would say you have to do it the second way to avoid a name clash.

When you define foo = property(..) it actually overrides the foo = models.. line, so that field will no longer be accessible.

You will need to use a different name for the property and the field. In fact, if you do it the way you have it in example #1 you will get an infinite loop when you try and access the property as it now tries to return itself.

EDIT: Perhaps you should also consider not using _foo as a field name, but rather foo, and then define another name for your property because properties cannot be used in QuerySet, so you'll need to use the actual field names when you do a filter for example.


As mentioned, a correct alternative to implementing your own django.db.models.Field class, one should use the db_column argument and a custom (or hidden) class attribute. I am just rewriting the code in the edit by @Jiaaro following more strict conventions for OOP in python (e.g. if _foo should be actually hidden):

class MyModel(models.Model):    __foo = models.CharField(max_length = 20, db_column='foo')    bar = models.CharField(max_length = 20)    @property    def foo(self):        if self.bar:            return self.bar        else:            return self.__foo    @foo.setter    def foo(self, value):        self.__foo = value

__foo will be resolved into _MyModel__foo (as seen by dir(..)) thus hidden (private). Note that this form also permits using of @property decorator which would be ultimately a nicer way to write readable code.

Again, django will create _MyModel table with two fields foo and bar.


The previous solutions suffer because @property causes problems in admin, and .filter(_foo).

A better solution would be to override setattr except that this can cause problems initializing the ORM object from the DB. However, there is a trick to get around this, and it's universal.

class MyModel(models.Model):    foo = models.CharField(max_length = 20)    bar = models.CharField(max_length = 20)    def __setattr__(self, attrname, val):        setter_func = 'setter_' + attrname        if attrname in self.__dict__ and callable(getattr(self, setter_func, None)):            super(MyModel, self).__setattr__(attrname, getattr(self, setter_func)(val))        else:            super(MyModel, self).__setattr__(attrname, val)    def setter_foo(self, val):        return val.upper()

The secret is 'attrname in self.__dict__'. When the model initializes either from new or hydrated from the __dict__!