Ticket #3148: django-3148-approach-via-descriptors.diff
File django-3148-approach-via-descriptors.diff, 5.4 KB (added by , 15 years ago) |
---|
-
django/db/models/fields/__init__.py
48 48 # 49 49 # getattr(obj, opts.pk.attname) 50 50 51 class FieldDescriptor(object): 52 """ 53 This gets added as descriptor for fields that implement 54 custom getter and/or setter decorators. 55 """ 56 def __init__(self, field): 57 self.field = field 58 59 def __get__(self, instance, owner): 60 if instance is None: 61 return self.field 62 if "__get__" in self.field._property: 63 return self.field._property["__get__"](instance) 64 return instance.__dict__[self.field.name] 65 66 def __set__(self, instance, value): 67 if instance is None: 68 return self.field 69 if "__set__" in self.field._property: 70 return self.field._property["__set__"](instance, value) 71 instance.__dict__[self.field.name] = value 72 51 73 class Field(object): 52 74 # Designates whether empty strings fundamentally are allowed at the 53 75 # database level. … … 97 119 self.creation_counter = Field.creation_counter 98 120 Field.creation_counter += 1 99 121 122 self._property = {} # stores kwargs for descriptors 123 100 124 def __cmp__(self, other): 101 125 # This is needed because bisect does not take a comparison function. 102 126 return cmp(self.creation_counter, other.creation_counter) … … 148 172 return self._unique or self.primary_key 149 173 unique = property(unique) 150 174 175 def setter(self, f): 176 self._property['__set__'] = f 177 return f 178 179 def getter(self, f): 180 self._property['__get__'] = f 181 return f 182 183 def deleter(self, f): 184 self._property['__delete__'] = f 185 return f 186 151 187 def set_attributes_from_name(self, name): 152 188 self.name = name 153 189 self.attname, self.column = self.get_attname_column() … … 160 196 if self.choices: 161 197 setattr(cls, 'get_%s_display' % self.name, curry(cls._get_FIELD_display, field=self)) 162 198 199 if self._property: 200 setattr(cls, name, FieldDescriptor(self)) 201 else: 202 setattr(cls, name, self) 203 163 204 def get_attname(self): 164 205 return self.name 165 206 -
tests/regressiontests/model_fields/tests.py
7 7 from django.core.exceptions import ValidationError 8 8 9 9 from models import Foo, Bar, Whiz, BigD, BigS, Image 10 from models import FooGetSet 10 11 11 12 try: 12 13 from decimal import Decimal … … 144 145 bs = BigS.objects.create(s = 'slug'*50) 145 146 bs = BigS.objects.get(pk=bs.pk) 146 147 self.assertEqual(bs.s, 'slug'*50) 148 149 class FieldProperties(django.test.TestCase): 150 def test_get_set(self): 151 """ 152 Test basic getter and setter functionality of a SlugField 153 """ 154 f = FooGetSet( s = "test" ) 155 self.assertEqual(f.s, "test") 156 f.s = "test2" 157 self.assertEqual(f.s, "test2") 158 159 def set_non_acceptable(): 160 f.s = "not_acceptable" 161 self.assertRaises(ValueError, set_non_acceptable) 162 self.assertEqual(f.s, "test2") 163 164 f.save() 165 f = FooGetSet.objects.get(pk=f.pk) 166 self.assertEqual(f.s, "test2") 167 168 self.assertEqual(f.file.__class__.__name__, "FieldFile") 169 170 # TODO: test that this does not go through FieldDescriptor 171 #self.assertEqual(f.b, '') 172 #self.assertFalse(hasattr(FooGetSet.b, '__get__')) 173 #self.assertTrue(hasattr(f.s, '__get__')) 174 175 def test_only_setter(self): 176 f = FooGetSet(only_setter = 1) 177 self.assertEqual(f.only_setter, 1) 178 f.only_setter = 10 179 self.assertEqual(f.only_setter, 10) 180 181 -
tests/regressiontests/model_fields/models.py
17 17 from django.db import models 18 18 from django.db.models.fields.files import ImageFieldFile, ImageField 19 19 20 temp_storage_location = tempfile.mkdtemp() 21 temp_storage = FileSystemStorage(location=temp_storage_location) 20 22 21 23 class Foo(models.Model): 22 24 a = models.CharField(max_length=10) … … 51 53 class BigS(models.Model): 52 54 s = models.SlugField(max_length=255) 53 55 56 class FooGetSet(models.Model): 57 s = models.SlugField(max_length=255) 58 file = models.FileField(storage=temp_storage, upload_to='tests') 59 b = models.CharField(max_length=10) 60 only_setter = models.CharField(max_length=10) 54 61 62 @s.getter 63 def get_s(self): 64 return self._s 65 @s.setter 66 def set_s(self, value): 67 if value is "not_acceptable": 68 raise ValueError("not acceptable") 69 self._s = value 70 71 @only_setter.setter 72 def set_only_setter(self, value): 73 self.set_through_setter = value 74 self.__dict__['only_setter'] = value 75 76 @file.getter 77 def get_file(self): 78 assert False 79 @file.setter 80 def set_file(self, value): 81 assert False 82 55 83 ############################################################################### 56 84 # ImageField 57 85