﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
3148	Add getters and setters to model fields	jerf@…		"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."	New feature	closed	Database layer (models, ORM)	dev	Normal	wontfix		Taiping jerf@… gav@… terrex2 ramusus@… semente+djangoproject@… erikrose Chris Chambers denilsonsa@… Christopher Grebs noah inactivist@…	Accepted	1	1	0	1	0	0
