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. |