Index: django/db/models/fields/__init__.py
===================================================================
--- django/db/models/fields/__init__.py	(Revision 11798)
+++ django/db/models/fields/__init__.py	(Arbeitskopie)
@@ -48,6 +48,28 @@
 #
 #     getattr(obj, opts.pk.attname)
 
+class FieldDescriptor(object):
+    """
+    This gets added as descriptor for fields that implement
+    custom getter and/or setter decorators.
+    """
+    def __init__(self, field):
+        self.field = field
+
+    def __get__(self, instance, owner):
+        if instance is None:
+            return self.field
+        if "__get__" in self.field._property:
+            return self.field._property["__get__"](instance)
+        return instance.__dict__[self.field.name]
+
+    def __set__(self, instance, value):
+        if instance is None:
+            return self.field
+        if "__set__" in self.field._property:
+            return self.field._property["__set__"](instance, value)
+        instance.__dict__[self.field.name] = value
+
 class Field(object):
     # Designates whether empty strings fundamentally are allowed at the
     # database level.
@@ -97,6 +119,8 @@
             self.creation_counter = Field.creation_counter
             Field.creation_counter += 1
 
+        self._property = {} #  stores kwargs for descriptors
+
     def __cmp__(self, other):
         # This is needed because bisect does not take a comparison function.
         return cmp(self.creation_counter, other.creation_counter)
@@ -148,6 +172,18 @@
         return self._unique or self.primary_key
     unique = property(unique)
 
+    def setter(self, f):
+        self._property['__set__'] = f
+        return f
+
+    def getter(self, f):
+        self._property['__get__'] = f
+        return f
+
+    def deleter(self, f):
+        self._property['__delete__'] = f
+        return f
+
     def set_attributes_from_name(self, name):
         self.name = name
         self.attname, self.column = self.get_attname_column()
@@ -160,6 +196,11 @@
         if self.choices:
             setattr(cls, 'get_%s_display' % self.name, curry(cls._get_FIELD_display, field=self))
 
+        if self._property:
+            setattr(cls, name, FieldDescriptor(self))
+        else:
+            setattr(cls, name, self)
+
     def get_attname(self):
         return self.name
 
Index: tests/regressiontests/model_fields/tests.py
===================================================================
--- tests/regressiontests/model_fields/tests.py	(Revision 11798)
+++ tests/regressiontests/model_fields/tests.py	(Arbeitskopie)
@@ -7,6 +7,7 @@
 from django.core.exceptions import ValidationError
 
 from models import Foo, Bar, Whiz, BigD, BigS, Image
+from models import FooGetSet
 
 try:
     from decimal import Decimal
@@ -144,3 +145,37 @@
         bs = BigS.objects.create(s = 'slug'*50)
         bs = BigS.objects.get(pk=bs.pk)
         self.assertEqual(bs.s, 'slug'*50)
+
+class FieldProperties(django.test.TestCase):
+    def test_get_set(self):
+        """
+        Test basic getter and setter functionality of a SlugField
+        """
+        f = FooGetSet( s = "test" )
+        self.assertEqual(f.s, "test")
+        f.s = "test2"
+        self.assertEqual(f.s, "test2")
+
+        def set_non_acceptable():
+            f.s = "not_acceptable"
+        self.assertRaises(ValueError, set_non_acceptable)
+        self.assertEqual(f.s, "test2")
+
+        f.save()
+        f = FooGetSet.objects.get(pk=f.pk)
+        self.assertEqual(f.s, "test2")
+
+        self.assertEqual(f.file.__class__.__name__, "FieldFile")
+
+        # TODO: test that this does not go through FieldDescriptor
+        #self.assertEqual(f.b, '')
+        #self.assertFalse(hasattr(FooGetSet.b, '__get__'))
+        #self.assertTrue(hasattr(f.s, '__get__'))
+
+    def test_only_setter(self):
+        f = FooGetSet(only_setter = 1)
+        self.assertEqual(f.only_setter, 1)
+        f.only_setter = 10
+        self.assertEqual(f.only_setter, 10)
+
+
Index: tests/regressiontests/model_fields/models.py
===================================================================
--- tests/regressiontests/model_fields/models.py	(Revision 11798)
+++ tests/regressiontests/model_fields/models.py	(Arbeitskopie)
@@ -17,6 +17,8 @@
 from django.db import models
 from django.db.models.fields.files import ImageFieldFile, ImageField
 
+temp_storage_location = tempfile.mkdtemp()
+temp_storage = FileSystemStorage(location=temp_storage_location)
 
 class Foo(models.Model):
     a = models.CharField(max_length=10)
@@ -51,7 +53,33 @@
 class BigS(models.Model):
     s = models.SlugField(max_length=255)
 
+class FooGetSet(models.Model):
+    s = models.SlugField(max_length=255)
+    file = models.FileField(storage=temp_storage, upload_to='tests')
+    b = models.CharField(max_length=10)
+    only_setter = models.CharField(max_length=10)
 
+    @s.getter
+    def get_s(self):
+        return self._s
+    @s.setter
+    def set_s(self, value):
+        if value is "not_acceptable":
+            raise ValueError("not acceptable")
+        self._s = value
+
+    @only_setter.setter
+    def set_only_setter(self, value):
+        self.set_through_setter = value
+        self.__dict__['only_setter'] = value
+
+    @file.getter
+    def get_file(self):
+        assert False
+    @file.setter
+    def set_file(self, value):
+        assert False
+
 ###############################################################################
 # ImageField
 
