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__!