Ticket #3148: 3148-propertyfield2.diff

File 3148-propertyfield2.diff, 5.9 KB (added by berdario, 8 years ago)

slightly more complex approach, that on the other hand manages to obtain the same behaviour with m2m fields

  • django/db/models/fields/__init__.py

    === modified file 'django/db/models/fields/__init__.py'
    old new  
    11831183        }
    11841184        defaults.update(kwargs)
    11851185        return super(URLField, self).formfield(**defaults)
     1186
     1187def property_field(field):
     1188    class PropertyField(property):
     1189        """property subclass to be used to "wrap" another Field, not a true Field per-se"""
     1190       
     1191        def __init__(self, fget=None, fset=None, fdel=None, doc=None):
     1192            super(PropertyField, self).__init__(fget, fset, fdel, doc)
     1193            self.field = field
     1194            self.creation_counter = Field.creation_counter
     1195            Field.creation_counter += 1
     1196           
     1197        def __getattr__(self, name):
     1198            return getattr(self.field, name)
     1199           
     1200        def contribute_to_class(self, cls, name):
     1201            self.name = name
     1202            cls._meta.add_property_field(self)
     1203            setattr(cls, name, self)
     1204   
     1205    return PropertyField
     1206
     1207   
     1208 No newline at end of file
  • django/db/models/options.py

    === modified file 'django/db/models/options.py'
    old new  
    170170
    171171    def add_virtual_field(self, field):
    172172        self.virtual_fields.append(field)
     173       
     174    def add_property_field(self, f):
     175        """
     176        Remove the original field wrapped by the property, and call again the field's
     177        contribute_to_class
     178        """
     179        if f.rel and isinstance(f.rel, ManyToManyRel):
     180            inner_rel = filter(lambda x: x.name == f.field.name, self.local_many_to_many)
     181            for r in inner_rel:
     182                self.local_many_to_many.remove(r)
     183        else:
     184            inner_fields = filter(lambda x: x.name == f.field.name, self.local_fields)
     185            for i_f in inner_fields:
     186                self.local_fields.remove(i_f)
     187           
     188        # hack needed to pass the unbound methods' type checking
     189        num_parents = len(type(f).__bases__)
     190        type(f).__bases__ += (type(f.field),)
     191        type(f.field).contribute_to_class(f, f.model, f.name)
     192        type(f).__bases__ = type(f).__bases__[:num_parents]
    173193
    174194    def setup_pk(self, field):
    175195        if not self.pk and field.primary_key:
  • tests/regressiontests/model_fields/models.py

    === modified file 'tests/regressiontests/model_fields/models.py'
    old new  
    1515
    1616from django.core.files.storage import FileSystemStorage
    1717from django.db import models
     18from django.db.models.fields import property_field
    1819from django.db.models.fields.files import ImageFieldFile, ImageField
    1920
    2021
     
    6667    bfield = models.BooleanField()
    6768    string = models.CharField(max_length=10, default='abc')
    6869
     70class FooGetSet(models.Model):
     71    _s = models.SlugField(max_length=255)
     72    _c = models.CharField(max_length=255)
     73
     74    @property_field(_s)
     75    def s(self):
     76        return self._s
     77    @s.setter
     78    def s(self, value):
     79        if value is "not_acceptable":
     80            raise ValueError("not acceptable")
     81        self._s = value
     82
     83
     84class GetSetSubclass(FooGetSet):
     85    @property_field(filter(lambda x: x.name == "_c", FooGetSet._meta.fields)[0])
     86    def c(self):
     87        return self._c[:-15]
     88   
     89    @c.setter
     90    def c(self, value):
     91        self._c = value + " useless foobar"
     92       
     93class GetSetRel(models.Model):
     94    _foos = models.ManyToManyField(FooGetSet)
     95   
     96    @property_field(_foos)
     97    def foos(self):
     98        return self._foos
     99   
     100    @foos.setter
     101    def foos(self, values):
     102        self._foos = [FooGetSet.objects.get_or_create(s=val)[0] for val in values]
     103
    69104###############################################################################
    70105# FileField
    71106
  • tests/regressiontests/model_fields/tests.py

    === modified file 'tests/regressiontests/model_fields/tests.py'
    old new  
    88from django.db.models.fields.files import FieldFile
    99from django.utils import unittest
    1010
    11 from models import Foo, Bar, Whiz, BigD, BigS, Image, BigInt, Post, NullBooleanModel, BooleanModel, Document
     11from models import Foo, Bar, Whiz, BigD, BigS, Image, BigInt, Post, NullBooleanModel, BooleanModel, Document, FooGetSet, GetSetSubclass, GetSetRel
    1212
    1313# If PIL available, do these tests.
    1414if Image:
     
    317317    def test_lookup_integer_in_textfield(self):
    318318        self.assertEqual(Post.objects.filter(body=24).count(), 0)
    319319
     320class FieldProperties(test.TestCase):
     321    def test_get_set(self):
     322        """
     323        Test basic property functionality of a SlugField
     324        """
     325        f = FooGetSet(s="test")
     326        self.assertEqual(f.s, "test")
     327        f.s = "test2"
     328        self.assertEqual(f.s, "test2")
     329
     330        def set_non_acceptable():
     331            f.s = "not_acceptable"
     332        self.assertRaises(ValueError, set_non_acceptable)
     333        self.assertEqual(f.s, "test2")
     334
     335        f.save()
     336        f = FooGetSet.objects.get(pk=f.pk)
     337        self.assertEqual(f.s, "test2")
     338   
     339    def test_inheritance(self):
     340        """
     341        Test a property that's working on an inherited field
     342        """
     343        sub = GetSetSubclass(s="foo", c="bar")
     344       
     345        self.assertEqual(sub._c, "bar useless foobar")
     346        self.assertEqual(sub.c, "bar")
     347   
     348    def test_m2m(self):
     349        """
     350        Test a property that's working on a many to many relationship
     351        """
     352        r = GetSetRel()
     353        r.save()
     354       
     355        r.foos = ["a", "b", "c", "d"]
     356        self.assertEqual(len(r.foos.all()), 4)
     357       
     358        self.assertTrue(r in FooGetSet.objects.get(s="a").getsetrel_set.all())
     359
    320360class FileFieldTests(unittest.TestCase):
    321361    def test_clearable(self):
    322362        """
Back to Top