Index: django/db/models/base.py
===================================================================
--- django/db/models/base.py	(revision 4218)
+++ django/db/models/base.py	(working copy)
@@ -90,6 +90,7 @@
 
     def __init__(self, *args, **kwargs):
         dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs)
+        self._django_initializing = True
         for f in self._meta.fields:
             if isinstance(f.rel, ManyToOneRel):
                 try:
@@ -127,6 +128,8 @@
             setattr(self, self._meta.fields[i].attname, arg)
         dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self)
 
+        del self._django_initializing
+
     def add_to_class(cls, name, value):
         if name == 'Admin':
             assert type(value) == types.ClassType, "%r attribute of %s model must be a class, not a %s object" % (name, cls.__name__, type(value))
Index: django/db/models/fields/__init__.py
===================================================================
--- django/db/models/fields/__init__.py	(revision 4218)
+++ django/db/models/fields/__init__.py	(working copy)
@@ -70,7 +70,7 @@
         core=False, rel=None, default=NOT_PROVIDED, editable=True,
         prepopulate_from=None, unique_for_date=None, unique_for_month=None,
         unique_for_year=None, validator_list=None, choices=None, radio_admin=None,
-        help_text='', db_column=None):
+        help_text='', db_column=None, getter=None, setter=None):
         self.name = name
         self.verbose_name = verbose_name
         self.primary_key = primary_key
@@ -86,6 +86,8 @@
         self.radio_admin = radio_admin
         self.help_text = help_text
         self.db_column = db_column
+        self.getter = getter
+        self.setter = setter
 
         # Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
         self.db_index = db_index
@@ -139,7 +141,37 @@
         cls._meta.add_field(self)
         if self.choices:
             setattr(cls, 'get_%s_display' % self.name, curry(cls._get_FIELD_display, field=self))
+        if self.setter is not None or self.getter is not None:
+            self.create_property_on_class(cls)
 
+    def create_property_on_class(self, cls):
+        """create the property to support setters and getters"""
+        name = self.name
+        field_att = "_%s" % name
+
+        if self.getter:
+            getter_name = self.getter
+            def get_func(self):
+                value = getattr(self, field_att)
+                return getattr(self, getter_name)(value)
+        else:
+            def get_func(self):
+                return getattr(self, field_att)
+
+        if self.setter:
+            setter_name = self.setter
+            def set_func(self, value):
+                if hasattr(self, '_django_initializing'):
+                    setattr(self, field_att, value)
+                else:
+                    setattr(self, field_att,
+                            getattr(self, setter_name)(value))
+        else:
+            def set_func(self, value):
+                setattr(self, field_att, value)
+
+        setattr(cls, name, property(get_func, set_func))
+
     def get_attname(self):
         return self.name
 
Index: tests/modeltests/getters_setters/__init__.py
===================================================================
Index: tests/modeltests/getters_setters/models.py
===================================================================
--- tests/modeltests/getters_setters/models.py	(revision 0)
+++ tests/modeltests/getters_setters/models.py	(revision 0)
@@ -0,0 +1,55 @@
+"""
+Test the 'getter' and 'setter' functionality on fields.
+
+This tests a field with a setter, a field with a getter, and a field
+with both. Since the getter/setter implementation is at the Field
+level, and I've verified that all Field objects pass through the code
+that constructs these properties, I believe it would be redundent to
+test all the Field Types individually.
+
+Doc note: Setters 
+"""
+
+from django.db import models
+
+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
+
+
+__test__ = {'API_TESTS':"""
+>>> a = GetSet(has_getter='x', has_setter='y', has_both='z')
+>>> a.has_getter
+'x_getter'
+>>> a.has_setter
+'y'
+>>> a.has_both
+'z_getter'
+
+# Setter has not run yet
+
+>>> a.updated_length_field
+0
+>>> a.has_both = 'abcd'
+>>> a.updated_length_field
+4
+>>> a.save()
+>>> a_id = a.id
+>>> del a
+>>> a = GetSet.objects.get(id=a_id)
+>>> a.updated_length_field
+4
+"""}
+                                  
+      
Index: docs/model-api.txt
===================================================================
--- docs/model-api.txt	(revision 4218)
+++ docs/model-api.txt	(working copy)
@@ -548,6 +548,16 @@
 processing using the object's ``AddManipulator`` or ``ChangeManipulator``
 classes. Default is ``True``.
 
+``getter``
+~~~~~~~~~~
+
+A string indicating a method of the class to call when you try
+to retrieve the value of the field. The method will be passed as its
+only argument the current "underlying value" of the field (as received
+from the database, object construction, or previous setting
+operation), and the return value of that method is what the user
+receives. 
+
 ``help_text``
 ~~~~~~~~~~~~~
 
@@ -581,6 +591,17 @@
 Don't use this for a field unless it's a ``ForeignKey`` or has ``choices``
 set.
 
+``setter``
+~~~~~~~~~~
+
+A string indicating a method of the class to call when you try to set
+the value of the field. The method will be passed as its only argument
+the value the user is setting the field to, and the return value is
+what will actually be set in the database. 
+
+The setter will not be run when the object is created,  either by
+database retrieval or construction.
+
 ``unique``
 ~~~~~~~~~~
 
