소스 검색

Removed SubfieldBase per deprecation timeline.

Tim Graham 9 년 전
부모
커밋
08ab262649

+ 0 - 1
django/db/models/__init__.py

@@ -12,7 +12,6 @@ from django.db.models.expressions import (  # NOQA
 from django.db.models.fields import *  # NOQA
 from django.db.models.fields.files import FileField, ImageField  # NOQA
 from django.db.models.fields.proxy import OrderWrt  # NOQA
-from django.db.models.fields.subclassing import SubfieldBase  # NOQA
 from django.db.models.lookups import Lookup, Transform  # NOQA
 from django.db.models.manager import Manager  # NOQA
 from django.db.models.query import Q, Prefetch, QuerySet  # NOQA

+ 0 - 63
django/db/models/fields/subclassing.py

@@ -1,63 +0,0 @@
-"""
-Convenience routines for creating non-trivial Field subclasses, as well as
-backwards compatibility utilities.
-
-Add SubfieldBase as the metaclass for your Field subclass, implement
-to_python() and the other necessary methods and everything will work
-seamlessly.
-"""
-
-import warnings
-
-from django.utils.deprecation import RemovedInDjango110Warning
-
-
-class SubfieldBase(type):
-    """
-    A metaclass for custom Field subclasses. This ensures the model's attribute
-    has the descriptor protocol attached to it.
-    """
-    def __new__(cls, name, bases, attrs):
-        warnings.warn("SubfieldBase has been deprecated. Use Field.from_db_value instead.",
-                  RemovedInDjango110Warning)
-
-        new_class = super(SubfieldBase, cls).__new__(cls, name, bases, attrs)
-        new_class.contribute_to_class = make_contrib(
-            new_class, attrs.get('contribute_to_class')
-        )
-        return new_class
-
-
-class Creator(object):
-    """
-    A placeholder class that provides a way to set the attribute on the model.
-    """
-    def __init__(self, field):
-        self.field = field
-
-    def __get__(self, obj, type=None):
-        if obj is None:
-            return self
-        return obj.__dict__[self.field.name]
-
-    def __set__(self, obj, value):
-        obj.__dict__[self.field.name] = self.field.to_python(value)
-
-
-def make_contrib(superclass, func=None):
-    """
-    Returns a suitable contribute_to_class() method for the Field subclass.
-
-    If 'func' is passed in, it is the existing contribute_to_class() method on
-    the subclass and it is called before anything else. It is assumed in this
-    case that the existing contribute_to_class() calls all the necessary
-    superclass methods.
-    """
-    def contribute_to_class(self, cls, name, **kwargs):
-        if func:
-            func(self, cls, name, **kwargs)
-        else:
-            super(superclass, self).contribute_to_class(cls, name, **kwargs)
-        setattr(cls, self.name, Creator(self))
-
-    return contribute_to_class

+ 0 - 7
docs/howto/custom-model-fields.txt

@@ -428,13 +428,6 @@ get out of the way.
 Converting values to Python objects
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. versionchanged:: 1.8
-
-    Historically, Django provided a metaclass called ``SubfieldBase`` which
-    always called :meth:`~Field.to_python` on assignment. This did not play
-    nicely with custom database transformations, aggregation, or values
-    queries, so it has been replaced with :meth:`~Field.from_db_value`.
-
 If your custom :class:`~Field` class deals with data structures that are more
 complex than strings, dates, integers, or floats, then you may need to override
 :meth:`~Field.from_db_value` and :meth:`~Field.to_python`.

+ 0 - 88
tests/field_subclassing/fields.py

@@ -1,94 +1,6 @@
 from __future__ import unicode_literals
 
-import json
-import warnings
-
 from django.db import models
-from django.utils import six
-from django.utils.deconstruct import deconstructible
-from django.utils.deprecation import RemovedInDjango110Warning
-from django.utils.encoding import force_text, python_2_unicode_compatible
-
-# Catch warning about subfieldbase  -- remove in Django 1.10
-warnings.filterwarnings(
-    'ignore',
-    'SubfieldBase has been deprecated. Use Field.from_db_value instead.',
-    RemovedInDjango110Warning
-)
-
-
-@deconstructible
-@python_2_unicode_compatible
-class Small(object):
-    """
-    A simple class to show that non-trivial Python objects can be used as
-    attributes.
-    """
-    def __init__(self, first, second):
-        self.first, self.second = first, second
-
-    def __str__(self):
-        return '%s%s' % (force_text(self.first), force_text(self.second))
-
-    def __eq__(self, other):
-        if isinstance(other, self.__class__):
-            return self.first == other.first and self.second == other.second
-        return False
-
-
-class SmallField(six.with_metaclass(models.SubfieldBase, models.Field)):
-    """
-    Turns the "Small" class into a Django field. Because of the similarities
-    with normal character fields and the fact that Small.__unicode__ does
-    something sensible, we don't need to implement a lot here.
-    """
-
-    def __init__(self, *args, **kwargs):
-        kwargs['max_length'] = 2
-        super(SmallField, self).__init__(*args, **kwargs)
-
-    def get_internal_type(self):
-        return 'CharField'
-
-    def to_python(self, value):
-        if isinstance(value, Small):
-            return value
-        return Small(value[0], value[1])
-
-    def get_db_prep_save(self, value, connection):
-        return six.text_type(value)
-
-    def get_prep_lookup(self, lookup_type, value):
-        if lookup_type == 'exact':
-            return force_text(value)
-        if lookup_type == 'in':
-            return [force_text(v) for v in value]
-        if lookup_type == 'isnull':
-            return []
-        raise TypeError('Invalid lookup type: %r' % lookup_type)
-
-
-class SmallerField(SmallField):
-    pass
-
-
-class JSONField(six.with_metaclass(models.SubfieldBase, models.TextField)):
-
-    description = ("JSONField automatically serializes and deserializes values to "
-        "and from JSON.")
-
-    def to_python(self, value):
-        if not value:
-            return None
-
-        if isinstance(value, six.string_types):
-            value = json.loads(value)
-        return value
-
-    def get_db_prep_save(self, value, connection):
-        if value is None:
-            return None
-        return json.dumps(value)
 
 
 class CustomTypedField(models.TextField):

+ 0 - 34
tests/field_subclassing/models.py

@@ -1,34 +0,0 @@
-"""
-Tests for field subclassing.
-"""
-from django.db import models
-from django.utils.encoding import force_text, python_2_unicode_compatible
-
-from .fields import JSONField, Small, SmallerField, SmallField
-
-
-@python_2_unicode_compatible
-class MyModel(models.Model):
-    name = models.CharField(max_length=10)
-    data = SmallField('small field')
-
-    def __str__(self):
-        return force_text(self.name)
-
-
-class OtherModel(models.Model):
-    data = SmallerField()
-
-
-class ChoicesModel(models.Model):
-    SMALL_AB = Small('a', 'b')
-    SMALL_CD = Small('c', 'd')
-    SMALL_CHOICES = (
-        (SMALL_AB, str(SMALL_AB)),
-        (SMALL_CD, str(SMALL_CD)),
-    )
-    data = SmallField('small field', choices=SMALL_CHOICES)
-
-
-class DataModel(models.Model):
-    data = JSONField()

+ 2 - 119
tests/field_subclassing/tests.py

@@ -1,126 +1,9 @@
 from __future__ import unicode_literals
 
-import inspect
-
-from django.core import exceptions, serializers
 from django.db import connection
-from django.test import SimpleTestCase, TestCase
-
-from .fields import CustomTypedField, Small
-from .models import ChoicesModel, DataModel, MyModel, OtherModel
-
-
-class CustomField(TestCase):
-    def test_refresh(self):
-        d = DataModel.objects.create(data=[1, 2, 3])
-        d.refresh_from_db(fields=['data'])
-        self.assertIsInstance(d.data, list)
-        self.assertEqual(d.data, [1, 2, 3])
-
-    def test_defer(self):
-        d = DataModel.objects.create(data=[1, 2, 3])
-
-        self.assertIsInstance(d.data, list)
-
-        d = DataModel.objects.get(pk=d.pk)
-        self.assertIsInstance(d.data, list)
-        self.assertEqual(d.data, [1, 2, 3])
-
-        d = DataModel.objects.defer("data").get(pk=d.pk)
-        self.assertIsInstance(d.data, list)
-        self.assertEqual(d.data, [1, 2, 3])
-        # Refetch for save
-        d = DataModel.objects.defer("data").get(pk=d.pk)
-        d.save()
-
-        d = DataModel.objects.get(pk=d.pk)
-        self.assertIsInstance(d.data, list)
-        self.assertEqual(d.data, [1, 2, 3])
-
-    def test_custom_field(self):
-        # Creating a model with custom fields is done as per normal.
-        s = Small(1, 2)
-        self.assertEqual(str(s), "12")
-
-        m = MyModel.objects.create(name="m", data=s)
-        # Custom fields still have normal field's attributes.
-        self.assertEqual(m._meta.get_field("data").verbose_name, "small field")
-
-        # The m.data attribute has been initialized correctly. It's a Small
-        # object.
-        self.assertEqual((m.data.first, m.data.second), (1, 2))
-
-        # The data loads back from the database correctly and 'data' has the
-        # right type.
-        m1 = MyModel.objects.get(pk=m.pk)
-        self.assertIsInstance(m1.data, Small)
-        self.assertEqual(str(m1.data), "12")
-
-        # We can do normal filtering on the custom field (and will get an error
-        # when we use a lookup type that does not make sense).
-        s1 = Small(1, 3)
-        s2 = Small("a", "b")
-        self.assertQuerysetEqual(
-            MyModel.objects.filter(data__in=[s, s1, s2]), [
-                "m",
-            ],
-            lambda m: m.name,
-        )
-        self.assertRaises(TypeError, lambda: MyModel.objects.filter(data__lt=s))
-
-        # Serialization works, too.
-        stream = serializers.serialize("json", MyModel.objects.all())
-        self.assertJSONEqual(stream, [{
-            "pk": m1.pk,
-            "model": "field_subclassing.mymodel",
-            "fields": {"data": "12", "name": "m"}
-        }])
-
-        obj = list(serializers.deserialize("json", stream))[0]
-        self.assertEqual(obj.object, m)
-
-        # Test retrieving custom field data
-        m.delete()
-
-        m1 = MyModel.objects.create(name="1", data=Small(1, 2))
-        MyModel.objects.create(name="2", data=Small(2, 3))
-
-        self.assertQuerysetEqual(
-            MyModel.objects.all(), [
-                "12",
-                "23",
-            ],
-            lambda m: str(m.data),
-            ordered=False
-        )
-
-    def test_field_subclassing(self):
-        o = OtherModel.objects.create(data=Small("a", "b"))
-        o = OtherModel.objects.get()
-        self.assertEqual(o.data.first, "a")
-        self.assertEqual(o.data.second, "b")
-
-    def test_subfieldbase_plays_nice_with_module_inspect(self):
-        """
-        Custom fields should play nice with python standard module inspect.
-
-        http://users.rcn.com/python/download/Descriptor.htm#properties
-        """
-        # Even when looking for totally different properties, SubfieldBase's
-        # non property like behavior made inspect crash. Refs #12568.
-        data = dict(inspect.getmembers(MyModel))
-        self.assertIn('__module__', data)
-        self.assertEqual(data['__module__'], 'field_subclassing.models')
-
-    def test_validation_of_choices_for_custom_field(self):
-        # a valid choice
-        o = ChoicesModel.objects.create(data=Small('a', 'b'))
-        o.full_clean()
+from django.test import SimpleTestCase
 
-        # an invalid choice
-        o = ChoicesModel.objects.create(data=Small('d', 'e'))
-        with self.assertRaises(exceptions.ValidationError):
-            o.full_clean()
+from .fields import CustomTypedField
 
 
 class TestDbType(SimpleTestCase):