Ticket #15184: foreignkey_subclass_patch.20141018.diff

File foreignkey_subclass_patch.20141018.diff, 4.6 KB (added by Luke Hodkinson, 10 years ago)

An updated version of the previous patch. This one works cleanly with the current master branch.

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

    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):  
    3232    """
    3333    A placeholder class that provides a way to set the attribute on the model.
    3434    """
    35     def __init__(self, field):
     35    def __init__(self, field, old_descr=None):
    3636        self.field = field
     37        self.old_descr = old_descr
    3738
    3839    def __get__(self, obj, type=None):
    3940        if obj is None:
    4041            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]
    4246
    4347    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
    4553
    4654
    4755def make_contrib(superclass, func=None):
    def make_contrib(superclass, func=None):  
    5866            func(self, cls, name, **kwargs)
    5967        else:
    6068            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))
    6274
    6375    return contribute_to_class
  • tests/field_subclassing/fields.py

    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)):  
    9494class CustomTypedField(models.TextField):
    9595    def db_type(self, connection):
    9696        return 'custom_field'
     97
     98
     99class 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
  • tests/field_subclassing/models.py

    diff --git a/tests/field_subclassing/models.py b/tests/field_subclassing/models.py
    index 66e765a..1ab2cb6 100644
    a b Tests for field subclassing.  
    44from django.db import models
    55from django.utils.encoding import force_text
    66
    7 from .fields import Small, SmallField, SmallerField, JSONField
     7from .fields import Small, SmallField, SmallerField, JSONField, FKSubField
    88from django.utils.encoding import python_2_unicode_compatible
    99
    1010
    class ChoicesModel(models.Model):  
    3333
    3434class DataModel(models.Model):
    3535    data = JSONField()
     36
     37
     38class FKModel(models.Model):
     39    data = FKSubField(MyModel)
  • tests/field_subclassing/tests.py

    diff --git a/tests/field_subclassing/tests.py b/tests/field_subclassing/tests.py
    index 5c695a4..f03e045 100644
    a b import inspect  
    55from django.core import exceptions, serializers
    66from django.db import connection
    77from django.test import TestCase
     8from django.core.exceptions import ObjectDoesNotExist
    89
    910from .fields import Small, CustomTypedField
    10 from .models import ChoicesModel, DataModel, MyModel, OtherModel
     11from .models import ChoicesModel, DataModel, MyModel, OtherModel, FKModel
    1112
    1213
    1314class CustomField(TestCase):
    class CustomField(TestCase):  
    9495        self.assertEqual(o.data.first, "a")
    9596        self.assertEqual(o.data.second, "b")
    9697
     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
    97116    def test_subfieldbase_plays_nice_with_module_inspect(self):
    98117        """
    99118        Custom fields should play nice with python standard module inspect.
Back to Top