diff --git a/django/db/models/fields/subclassing.py b/django/db/models/fields/subclassing.py
index 4233106..ce6a760 100644
|
a
|
b
|
class Creator(object):
|
| 32 | 32 | """ |
| 33 | 33 | A placeholder class that provides a way to set the attribute on the model. |
| 34 | 34 | """ |
| 35 | | def __init__(self, field): |
| | 35 | def __init__(self, field, old_descr=None): |
| 36 | 36 | self.field = field |
| | 37 | self.old_descr = old_descr |
| 37 | 38 | |
| 38 | 39 | def __get__(self, obj, type=None): |
| 39 | 40 | if obj is None: |
| 40 | 41 | return self |
| 41 | | return obj.__dict__[self.field.name] |
| | 42 | if self.old_descr: |
| | 43 | return self.old_descr.__get__(obj, type) |
| | 44 | else: |
| | 45 | return obj.__dict__[self.field.name] |
| 42 | 46 | |
| 43 | 47 | def __set__(self, obj, value): |
| 44 | | obj.__dict__[self.field.name] = self.field.to_python(value) |
| | 48 | val = self.field.to_python(value) |
| | 49 | if self.old_descr: |
| | 50 | self.old_descr.__set__(obj, val) |
| | 51 | else: |
| | 52 | obj.__dict__[self.field.name] = val |
| 45 | 53 | |
| 46 | 54 | |
| 47 | 55 | def make_contrib(superclass, func=None): |
| … |
… |
def make_contrib(superclass, func=None):
|
| 58 | 66 | func(self, cls, name, **kwargs) |
| 59 | 67 | else: |
| 60 | 68 | super(superclass, self).contribute_to_class(cls, name, **kwargs) |
| 61 | | setattr(cls, self.name, Creator(self)) |
| | 69 | # If this value already exists on the class it is likely a descriptor |
| | 70 | # for related fields. Keep it around so we can call it from our |
| | 71 | # descriptor. |
| | 72 | old_descr = cls.__dict__.get(self.name) |
| | 73 | setattr(cls, self.name, Creator(self, old_descr)) |
| 62 | 74 | |
| 63 | 75 | return contribute_to_class |
diff --git a/tests/field_subclassing/fields.py b/tests/field_subclassing/fields.py
index b94b237..9d1af76 100644
|
a
|
b
|
class JSONField(six.with_metaclass(models.SubfieldBase, models.TextField)):
|
| 94 | 94 | class CustomTypedField(models.TextField): |
| 95 | 95 | def db_type(self, connection): |
| 96 | 96 | return 'custom_field' |
| | 97 | |
| | 98 | |
| | 99 | class FKSubField(models.ForeignKey): |
| | 100 | """ |
| | 101 | Subclass ForeignKey to check descriptor overloading. Confirms ticket |
| | 102 | #15184 has been corrected. |
| | 103 | """ |
| | 104 | __metaclass__ = models.SubfieldBase |
| | 105 | |
| | 106 | def __init__(self, cls, *args, **kwargs): |
| | 107 | super(FKSubField, self).__init__(cls, *args, **kwargs) |
| | 108 | |
| | 109 | def to_python(self, value): |
| | 110 | return value |
diff --git a/tests/field_subclassing/models.py b/tests/field_subclassing/models.py
index 66e765a..1ab2cb6 100644
|
a
|
b
|
Tests for field subclassing.
|
| 4 | 4 | from django.db import models |
| 5 | 5 | from django.utils.encoding import force_text |
| 6 | 6 | |
| 7 | | from .fields import Small, SmallField, SmallerField, JSONField |
| | 7 | from .fields import Small, SmallField, SmallerField, JSONField, FKSubField |
| 8 | 8 | from django.utils.encoding import python_2_unicode_compatible |
| 9 | 9 | |
| 10 | 10 | |
| … |
… |
class ChoicesModel(models.Model):
|
| 33 | 33 | |
| 34 | 34 | class DataModel(models.Model): |
| 35 | 35 | data = JSONField() |
| | 36 | |
| | 37 | |
| | 38 | class FKModel(models.Model): |
| | 39 | data = FKSubField(MyModel) |
diff --git a/tests/field_subclassing/tests.py b/tests/field_subclassing/tests.py
index 5c695a4..f03e045 100644
|
a
|
b
|
import inspect
|
| 5 | 5 | from django.core import exceptions, serializers |
| 6 | 6 | from django.db import connection |
| 7 | 7 | from django.test import TestCase |
| | 8 | from django.core.exceptions import ObjectDoesNotExist |
| 8 | 9 | |
| 9 | 10 | from .fields import Small, CustomTypedField |
| 10 | | from .models import ChoicesModel, DataModel, MyModel, OtherModel |
| | 11 | from .models import ChoicesModel, DataModel, MyModel, OtherModel, FKModel |
| 11 | 12 | |
| 12 | 13 | |
| 13 | 14 | class CustomField(TestCase): |
| … |
… |
class CustomField(TestCase):
|
| 94 | 95 | self.assertEqual(o.data.first, "a") |
| 95 | 96 | self.assertEqual(o.data.second, "b") |
| 96 | 97 | |
| | 98 | def test_foreignkey_subclassing(self): |
| | 99 | obj = FKModel() |
| | 100 | |
| | 101 | # We have to do something a bit funky to catch this exception due to |
| | 102 | # it originating in a descriptor. |
| | 103 | okay = False |
| | 104 | try: |
| | 105 | # The next line would have raised a KeyError prior to being fixed. |
| | 106 | val = obj.data |
| | 107 | except ObjectDoesNotExist: |
| | 108 | okay = True |
| | 109 | self.assertEquals(okay, True) |
| | 110 | |
| | 111 | target = MyModel.objects.create(name="1", data=Small(1, 2)) |
| | 112 | obj.data = target |
| | 113 | obj.save() |
| | 114 | self.assertEqual(obj.data, target) |
| | 115 | |
| 97 | 116 | def test_subfieldbase_plays_nice_with_module_inspect(self): |
| 98 | 117 | """ |
| 99 | 118 | Custom fields should play nice with python standard module inspect. |