Add getters and setters to model fields
|Reported by:||jerf@…||Owned by:|
|Component:||Database layer (models, ORM)||Version:||master|
|Cc:||Taiping, jerf@…, gav@…, terrex2, ramusus@…, semente+djangoproject@…, erikrose, chrischambers, denilsonsa@…, EnTeQuAk, noah, inactivist@…||Triage Stage:||Accepted|
|Has patch:||yes||Needs documentation:||yes|
|Needs tests:||no||Patch needs improvement:||yes|
Whenever you have two distinct ways to update a variable, you introduce the opportunity to have bugs. This becomes increasingly true as the system grows in size.
It is often the case that when changing one field on an object, you want to be able to automatically run code of some kind; this is the same basic motivation behind the "property" builtin in Python. However, the obvious way of doing that in Django doesn't work:
class Something(models.Model): field = models.BooleanField(...) ... def set_field(self, value): # do something field = property(set_field)
The second field overrides the first, and in the process of constructing the model Django never gets a chance to see the models.BooleanField.
This patch adds a 'getter' and 'setter' attribute to all fields, which takes a string of a method to call when a field is retrieved or set. It turns out that it is fairly easy to add a property to the class during Django's initialization, at which point it has already retrieved the field information. This example from the enclosed tests shows the basics of its usage:
class GetSet(models.Model): has_getter = models.CharField(maxlength=20, getter='simple_getter') has_setter = models.CharField(maxlength=20, setter='simple_setter') has_both = models.CharField(maxlength=20, getter='simple_getter', setter='updater') updated_length_field = models.IntegerField(default=0) def simple_getter(self, value): return value + "_getter" def simple_setter(self, value): return value + "_setter" def updater(self, value): self.updated_length_field = len(value) return value
This defines a getter on has_getter that returns a filtered value from the DB, a setter on has_setter that processes the value to add "_setter" to it in all cases, and on has_both we see a setter than implements the use case of updating another field when a property is set. (As is often the case, this is a trivial example; in my real code, for instance, the value being updated is actually in a related object.)
A getter receives as its argument the current "real" value (the one that either came from the database, object construction, or a prior setting of the value), and what the getter returns is actually what the user gets back from the attribute.
A setter receives as its argument the value that the user is setting the property to, and what it returns is what the property will actually be set to. The pattern for just using that as a hook is to return what is passed in after you've done whatever it is your hook does, as shown above.
These properties are only created for fields that have getters or setters, so the backwards-compatibility impact of this should be zero, and the performance impact should be a check for getters/setters per field once at startup, which is minimal. I'm a little less certain about exactly how these getters and setters will interact with all the various field types, but due to the way in which it hooks in it should, in theory, have minimal impact, because no Python code should fail to go through the property.
Getters and setters do not operate during the creation of the object, be it by retrieval from the database or creation by instantiating the class; this avoids a lot of tricky issues with property initialization order and double-application of some common setters, but will need to be documented.
I'd be happy to add the appropriate documentation but I do not see where to contribute that.
Change History (65)
comment:3 Changed 8 years ago by Simon G. <dev@…>
- Triage Stage changed from Unreviewed to Design decision needed
comment:4 Changed 8 years ago by enrobberors
- Cc Taiping added; jerf@… removed
- Keywords MESSAGE added
comment:8 Changed 7 years ago by telenieko
- Owner changed from nobody to telenieko
- Status changed from new to assigned
- Summary changed from [patch] Add getters and setters to model fields to Add getters and setters to model fields
- Version set to SVN
comment:9 Changed 7 years ago by telenieko
- Triage Stage changed from Design decision needed to Accepted
comment:11 in reply to: ↑ 10 ; follow-up: ↓ 12 Changed 7 years ago by telenieko
- Keywords MESSAGE removed
Changed 7 years ago by terrex2
comment:25 in reply to: ↑ 24 Changed 6 years ago by Gulopine
- Owner changed from telenieko to Gulopine
- Patch needs improvement set
- Status changed from assigned to new
comment:37 Changed 5 years ago by Guilherme Gondim (semente) <semente@…>
- Cc semente+djangoproject@… added
comment:39 Changed 5 years ago by Alex
- Owner changed from Gulopine to Alex
- Status changed from new to assigned
comment:45 Changed 4 years ago by lrekucki
- Severity changed from normal to Normal
- Type changed from enhancement to New feature
comment:49 Changed 3 years ago by aaugustin
- Resolution set to wontfix
- Status changed from new to closed