=== modified file 'django/db/models/fields/__init__.py'
--- old/django/db/models/fields/__init__.py	2011-08-31 08:09:51 +0000
+++ new/django/db/models/fields/__init__.py	2011-08-31 17:23:39 +0000
@@ -1183,3 +1183,25 @@
         }
         defaults.update(kwargs)
         return super(URLField, self).formfield(**defaults)
+
+def property_field(field):
+    class PropertyField(property):
+        """property subclass to be used to "wrap" another Field, not a true Field per-se"""
+        
+        def __init__(self, fget=None, fset=None, fdel=None, doc=None):
+            super(PropertyField, self).__init__(fget, fset, fdel, doc)
+            self.field = field
+            self.creation_counter = Field.creation_counter
+            Field.creation_counter += 1
+            
+        def __getattr__(self, name):
+            return getattr(self.field, name)
+            
+        def contribute_to_class(self, cls, name):
+            self.name = name
+            cls._meta.add_property_field(self)
+            setattr(cls, name, self)
+    
+    return PropertyField
+
+    
\ No newline at end of file

=== modified file 'django/db/models/options.py'
--- old/django/db/models/options.py	2011-04-27 17:51:43 +0000
+++ new/django/db/models/options.py	2011-09-01 12:36:48 +0000
@@ -170,6 +170,26 @@
 
     def add_virtual_field(self, field):
         self.virtual_fields.append(field)
+        
+    def add_property_field(self, f):
+        """
+        Remove the original field wrapped by the property, and call again the field's
+        contribute_to_class
+        """
+        if f.rel and isinstance(f.rel, ManyToManyRel):
+            inner_rel = filter(lambda x: x.name == f.field.name, self.local_many_to_many)
+            for r in inner_rel:
+                self.local_many_to_many.remove(r)
+        else:
+            inner_fields = filter(lambda x: x.name == f.field.name, self.local_fields)
+            for i_f in inner_fields:
+                self.local_fields.remove(i_f)
+            
+        # hack needed to pass the unbound methods' type checking
+        num_parents = len(type(f).__bases__)
+        type(f).__bases__ += (type(f.field),)
+        type(f.field).contribute_to_class(f, f.model, f.name)
+        type(f).__bases__ = type(f).__bases__[:num_parents]
 
     def setup_pk(self, field):
         if not self.pk and field.primary_key:

=== modified file 'tests/regressiontests/model_fields/models.py'
--- old/tests/regressiontests/model_fields/models.py	2011-08-31 08:09:51 +0000
+++ new/tests/regressiontests/model_fields/models.py	2011-08-31 17:30:52 +0000
@@ -15,6 +15,7 @@
 
 from django.core.files.storage import FileSystemStorage
 from django.db import models
+from django.db.models.fields import property_field
 from django.db.models.fields.files import ImageFieldFile, ImageField
 
 
@@ -66,6 +67,40 @@
     bfield = models.BooleanField()
     string = models.CharField(max_length=10, default='abc')
 
+class FooGetSet(models.Model):
+    _s = models.SlugField(max_length=255)
+    _c = models.CharField(max_length=255)
+
+    @property_field(_s)
+    def s(self):
+        return self._s
+    @s.setter
+    def s(self, value):
+        if value is "not_acceptable":
+            raise ValueError("not acceptable")
+        self._s = value
+
+
+class GetSetSubclass(FooGetSet):
+    @property_field(filter(lambda x: x.name == "_c", FooGetSet._meta.fields)[0])
+    def c(self):
+        return self._c[:-15]
+    
+    @c.setter
+    def c(self, value):
+        self._c = value + " useless foobar"
+        
+class GetSetRel(models.Model):
+    _foos = models.ManyToManyField(FooGetSet)
+    
+    @property_field(_foos)
+    def foos(self):
+        return self._foos
+    
+    @foos.setter
+    def foos(self, values):
+        self._foos = [FooGetSet.objects.get_or_create(s=val)[0] for val in values]
+
 ###############################################################################
 # FileField
 

=== modified file 'tests/regressiontests/model_fields/tests.py'
--- old/tests/regressiontests/model_fields/tests.py	2011-08-31 08:09:51 +0000
+++ new/tests/regressiontests/model_fields/tests.py	2011-08-31 17:39:57 +0000
@@ -8,7 +8,7 @@
 from django.db.models.fields.files import FieldFile
 from django.utils import unittest
 
-from models import Foo, Bar, Whiz, BigD, BigS, Image, BigInt, Post, NullBooleanModel, BooleanModel, Document
+from models import Foo, Bar, Whiz, BigD, BigS, Image, BigInt, Post, NullBooleanModel, BooleanModel, Document, FooGetSet, GetSetSubclass, GetSetRel
 
 # If PIL available, do these tests.
 if Image:
@@ -317,6 +317,46 @@
     def test_lookup_integer_in_textfield(self):
         self.assertEqual(Post.objects.filter(body=24).count(), 0)
 
+class FieldProperties(test.TestCase):
+    def test_get_set(self):
+        """
+        Test basic property 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")
+    
+    def test_inheritance(self):
+        """
+        Test a property that's working on an inherited field
+        """
+        sub = GetSetSubclass(s="foo", c="bar")
+        
+        self.assertEqual(sub._c, "bar useless foobar")
+        self.assertEqual(sub.c, "bar")
+    
+    def test_m2m(self):
+        """
+        Test a property that's working on a many to many relationship
+        """
+        r = GetSetRel()
+        r.save()
+        
+        r.foos = ["a", "b", "c", "d"]
+        self.assertEqual(len(r.foos.all()), 4)
+        
+        self.assertTrue(r in FooGetSet.objects.get(s="a").getsetrel_set.all())
+
 class FileFieldTests(unittest.TestCase):
     def test_clearable(self):
         """

