Parcourir la source

Fixed #28370 -- Deprecated the context arg of Field.from_db_value() and Expression.convert_value().

Unused since a0d166306fbdc41f49e6fadf4ec84b17eb147daa.
Tim Graham il y a 7 ans
Parent
commit
487362fa8f

+ 1 - 1
django/contrib/gis/db/backends/mysql/operations.py

@@ -95,7 +95,7 @@ class MySQLOperations(BaseSpatialOperations, DatabaseOperations):
 
     # https://dev.mysql.com/doc/refman/en/spatial-function-argument-handling.html
     # MySQL 5.7.5 adds support for the empty geometry collections, but they are represented with invalid WKT.
-    def convert_invalid_empty_geometry_collection(self, value, expression, connection, context):
+    def convert_invalid_empty_geometry_collection(self, value, expression, connection):
         if value == b'GEOMETRYCOLLECTION()':
             return b'GEOMETRYCOLLECTION EMPTY'
         return value

+ 2 - 2
django/contrib/gis/db/models/aggregates.py

@@ -43,7 +43,7 @@ class Extent(GeoAggregate):
     def __init__(self, expression, **extra):
         super().__init__(expression, output_field=ExtentField(), **extra)
 
-    def convert_value(self, value, expression, connection, context):
+    def convert_value(self, value, expression, connection):
         return connection.ops.convert_extent(value)
 
 
@@ -54,7 +54,7 @@ class Extent3D(GeoAggregate):
     def __init__(self, expression, **extra):
         super().__init__(expression, output_field=ExtentField(), **extra)
 
-    def convert_value(self, value, expression, connection, context):
+    def convert_value(self, value, expression, connection):
         return connection.ops.convert_extent3d(value)
 
 

+ 2 - 2
django/contrib/gis/db/models/fields.py

@@ -255,7 +255,7 @@ class GeometryField(GeoSelectFormatMixin, BaseSpatialField):
             kwargs['geography'] = self.geography
         return name, path, args, kwargs
 
-    def from_db_value(self, value, expression, connection, context):
+    def from_db_value(self, value, expression, connection):
         if value:
             value = Geometry(value)
             srid = value.srid
@@ -351,7 +351,7 @@ class RasterField(BaseSpatialField):
         self._check_connection(connection)
         return super().db_type(connection)
 
-    def from_db_value(self, value, expression, connection, context):
+    def from_db_value(self, value, expression, connection):
         return connection.ops.parse_raster(value)
 
     def contribute_to_class(self, cls, name, **kwargs):

+ 2 - 2
django/contrib/gis/db/models/sql/conversion.py

@@ -23,7 +23,7 @@ class AreaField(models.FloatField):
             return value
         return getattr(value, self.area_att)
 
-    def from_db_value(self, value, expression, connection, context):
+    def from_db_value(self, value, expression, connection):
         # If the database returns a Decimal, convert it to a float as expected
         # by the Python geometric objects.
         if isinstance(value, Decimal):
@@ -54,7 +54,7 @@ class DistanceField(models.FloatField):
             raise ValueError('Distance measure is supplied, but units are unknown for result.')
         return getattr(value, self.distance_att)
 
-    def from_db_value(self, value, expression, connection, context):
+    def from_db_value(self, value, expression, connection):
         if value is None or not self.distance_att:
             return value
         return Distance(**{self.distance_att: value})

+ 3 - 3
django/contrib/postgres/aggregates/general.py

@@ -13,7 +13,7 @@ class ArrayAgg(Aggregate):
     def __init__(self, expression, distinct=False, **extra):
         super().__init__(expression, distinct='DISTINCT ' if distinct else '', **extra)
 
-    def convert_value(self, value, expression, connection, context):
+    def convert_value(self, value, expression, connection):
         if not value:
             return []
         return value
@@ -39,7 +39,7 @@ class JSONBAgg(Aggregate):
     function = 'JSONB_AGG'
     output_field = JSONField()
 
-    def convert_value(self, value, expression, connection, context):
+    def convert_value(self, value, expression, connection):
         if not value:
             return []
         return value
@@ -53,7 +53,7 @@ class StringAgg(Aggregate):
         distinct = 'DISTINCT ' if distinct else ''
         super().__init__(expression, delimiter=delimiter, distinct=distinct, **extra)
 
-    def convert_value(self, value, expression, connection, context):
+    def convert_value(self, value, expression, connection):
         if not value:
             return ''
         return value

+ 1 - 1
django/contrib/postgres/aggregates/statistics.py

@@ -41,7 +41,7 @@ class RegrCount(StatAggregate):
     def __init__(self, y, x):
         super().__init__(y=y, x=x, output_field=IntegerField())
 
-    def convert_value(self, value, expression, connection, context):
+    def convert_value(self, value, expression, connection):
         if value is None:
             return 0
         return int(value)

+ 5 - 2
django/contrib/postgres/fields/array.py

@@ -6,6 +6,7 @@ from django.contrib.postgres.validators import ArrayMaxLengthValidator
 from django.core import checks, exceptions
 from django.db.models import Field, IntegerField, Transform
 from django.db.models.lookups import Exact, In
+from django.utils.inspect import func_supports_parameter
 from django.utils.translation import gettext_lazy as _
 
 from ..utils import prefix_validation_error
@@ -103,11 +104,13 @@ class ArrayField(Field):
             value = [self.base_field.to_python(val) for val in vals]
         return value
 
-    def _from_db_value(self, value, expression, connection, context):
+    def _from_db_value(self, value, expression, connection):
         if value is None:
             return value
         return [
-            self.base_field.from_db_value(item, expression, connection, context)
+            self.base_field.from_db_value(item, expression, connection, {})
+            if func_supports_parameter(self.base_field.from_db_value, 'context')  # RemovedInDjango30Warning
+            else self.base_field.from_db_value(item, expression, connection)
             for item in value
         ]
 

+ 9 - 2
django/core/cache/backends/db.py

@@ -8,6 +8,7 @@ from django.core.cache.backends.base import DEFAULT_TIMEOUT, BaseCache
 from django.db import DatabaseError, connections, models, router, transaction
 from django.utils import timezone
 from django.utils.encoding import force_bytes
+from django.utils.inspect import func_supports_parameter
 
 
 class Options:
@@ -64,7 +65,10 @@ class DatabaseCache(BaseDatabaseCache):
         expression = models.Expression(output_field=models.DateTimeField())
         for converter in (connection.ops.get_db_converters(expression) +
                           expression.get_db_converters(connection)):
-            expires = converter(expires, expression, connection, {})
+            if func_supports_parameter(converter, 'context'):  # RemovedInDjango30Warning
+                expires = converter(expires, expression, connection, {})
+            else:
+                expires = converter(expires, expression, connection)
 
         if expires < timezone.now():
             db = router.db_for_write(self.cache_model_class)
@@ -126,7 +130,10 @@ class DatabaseCache(BaseDatabaseCache):
                         expression = models.Expression(output_field=models.DateTimeField())
                         for converter in (connection.ops.get_db_converters(expression) +
                                           expression.get_db_converters(connection)):
-                            current_expires = converter(current_expires, expression, connection, {})
+                            if func_supports_parameter(converter, 'context'):  # RemovedInDjango30Warning
+                                current_expires = converter(current_expires, expression, connection, {})
+                            else:
+                                current_expires = converter(current_expires, expression, connection)
 
                     exp = connection.ops.adapt_datetimefield_value(exp)
                     if result and (mode == 'set' or (mode == 'add' and current_expires < now)):

+ 1 - 1
django/db/backends/base/operations.py

@@ -534,7 +534,7 @@ class BaseDatabaseOperations:
         """
         return []
 
-    def convert_durationfield_value(self, value, expression, connection, context):
+    def convert_durationfield_value(self, value, expression, connection):
         if value is not None:
             value = str(decimal.Decimal(value) / decimal.Decimal(1000000))
             value = parse_duration(value)

+ 4 - 4
django/db/backends/mysql/operations.py

@@ -221,23 +221,23 @@ class DatabaseOperations(BaseDatabaseOperations):
             converters.append(self.convert_uuidfield_value)
         return converters
 
-    def convert_textfield_value(self, value, expression, connection, context):
+    def convert_textfield_value(self, value, expression, connection):
         if value is not None:
             value = force_text(value)
         return value
 
-    def convert_booleanfield_value(self, value, expression, connection, context):
+    def convert_booleanfield_value(self, value, expression, connection):
         if value in (0, 1):
             value = bool(value)
         return value
 
-    def convert_datetimefield_value(self, value, expression, connection, context):
+    def convert_datetimefield_value(self, value, expression, connection):
         if value is not None:
             if settings.USE_TZ:
                 value = timezone.make_aware(value, self.connection.timezone)
         return value
 
-    def convert_uuidfield_value(self, value, expression, connection, context):
+    def convert_uuidfield_value(self, value, expression, connection):
         if value is not None:
             value = uuid.UUID(value)
         return value

+ 8 - 8
django/db/backends/oracle/operations.py

@@ -165,17 +165,17 @@ END;
         converters.append(self.convert_empty_values)
         return converters
 
-    def convert_textfield_value(self, value, expression, connection, context):
+    def convert_textfield_value(self, value, expression, connection):
         if isinstance(value, Database.LOB):
             value = value.read()
         return value
 
-    def convert_binaryfield_value(self, value, expression, connection, context):
+    def convert_binaryfield_value(self, value, expression, connection):
         if isinstance(value, Database.LOB):
             value = force_bytes(value.read())
         return value
 
-    def convert_booleanfield_value(self, value, expression, connection, context):
+    def convert_booleanfield_value(self, value, expression, connection):
         if value in (0, 1):
             value = bool(value)
         return value
@@ -184,28 +184,28 @@ END;
     # DATE and TIMESTAMP columns, but Django wants to see a
     # python datetime.date, .time, or .datetime.
 
-    def convert_datetimefield_value(self, value, expression, connection, context):
+    def convert_datetimefield_value(self, value, expression, connection):
         if value is not None:
             if settings.USE_TZ:
                 value = timezone.make_aware(value, self.connection.timezone)
         return value
 
-    def convert_datefield_value(self, value, expression, connection, context):
+    def convert_datefield_value(self, value, expression, connection):
         if isinstance(value, Database.Timestamp):
             value = value.date()
         return value
 
-    def convert_timefield_value(self, value, expression, connection, context):
+    def convert_timefield_value(self, value, expression, connection):
         if isinstance(value, Database.Timestamp):
             value = value.time()
         return value
 
-    def convert_uuidfield_value(self, value, expression, connection, context):
+    def convert_uuidfield_value(self, value, expression, connection):
         if value is not None:
             value = uuid.UUID(value)
         return value
 
-    def convert_empty_values(self, value, expression, connection, context):
+    def convert_empty_values(self, value, expression, connection):
         # Oracle stores empty strings as null. We need to undo this in
         # order to adhere to the Django convention of using the empty
         # string instead of null, but only if the field accepts the

+ 6 - 6
django/db/backends/sqlite3/operations.py

@@ -212,7 +212,7 @@ class DatabaseOperations(BaseDatabaseOperations):
             converters.append(self.convert_booleanfield_value)
         return converters
 
-    def convert_datetimefield_value(self, value, expression, connection, context):
+    def convert_datetimefield_value(self, value, expression, connection):
         if value is not None:
             if not isinstance(value, datetime.datetime):
                 value = parse_datetime(value)
@@ -220,30 +220,30 @@ class DatabaseOperations(BaseDatabaseOperations):
                 value = timezone.make_aware(value, self.connection.timezone)
         return value
 
-    def convert_datefield_value(self, value, expression, connection, context):
+    def convert_datefield_value(self, value, expression, connection):
         if value is not None:
             if not isinstance(value, datetime.date):
                 value = parse_date(value)
         return value
 
-    def convert_timefield_value(self, value, expression, connection, context):
+    def convert_timefield_value(self, value, expression, connection):
         if value is not None:
             if not isinstance(value, datetime.time):
                 value = parse_time(value)
         return value
 
-    def convert_decimalfield_value(self, value, expression, connection, context):
+    def convert_decimalfield_value(self, value, expression, connection):
         if value is not None:
             value = expression.output_field.format_number(value)
             value = backend_utils.typecast_decimal(value)
         return value
 
-    def convert_uuidfield_value(self, value, expression, connection, context):
+    def convert_uuidfield_value(self, value, expression, connection):
         if value is not None:
             value = uuid.UUID(value)
         return value
 
-    def convert_booleanfield_value(self, value, expression, connection, context):
+    def convert_booleanfield_value(self, value, expression, connection):
         return bool(value) if value in (1, 0) else value
 
     def bulk_insert_sql(self, fields, placeholder_rows):

+ 3 - 3
django/db/models/aggregates.py

@@ -73,7 +73,7 @@ class Count(Aggregate):
     def _get_repr_options(self):
         return {'distinct': self.extra['distinct'] != ''}
 
-    def convert_value(self, value, expression, connection, context):
+    def convert_value(self, value, expression, connection):
         if value is None:
             return 0
         return int(value)
@@ -99,7 +99,7 @@ class StdDev(Aggregate):
     def _get_repr_options(self):
         return {'sample': self.function == 'STDDEV_SAMP'}
 
-    def convert_value(self, value, expression, connection, context):
+    def convert_value(self, value, expression, connection):
         if value is None:
             return value
         return float(value)
@@ -129,7 +129,7 @@ class Variance(Aggregate):
     def _get_repr_options(self):
         return {'sample': self.function == 'VAR_SAMP'}
 
-    def convert_value(self, value, expression, connection, context):
+    def convert_value(self, value, expression, connection):
         if value is None:
             return value
         return float(value)

+ 1 - 1
django/db/models/expressions.py

@@ -261,7 +261,7 @@ class BaseExpression:
                 raise FieldError('Expression contains mixed types. You must set output_field.')
             return output_field
 
-    def convert_value(self, value, expression, connection, context):
+    def convert_value(self, value, expression, connection):
         """
         Expressions provide their own converters because users have the option
         of manually specifying the output_field which may be a different type

+ 1 - 1
django/db/models/fields/related.py

@@ -939,7 +939,7 @@ class ForeignKey(ForeignObject):
     def db_parameters(self, connection):
         return {"type": self.db_type(connection), "check": self.db_check(connection)}
 
-    def convert_empty_strings(self, value, expression, connection, context):
+    def convert_empty_strings(self, value, expression, connection):
         if (not value) and isinstance(value, str):
             return None
         return value

+ 1 - 1
django/db/models/functions/datetime.py

@@ -198,7 +198,7 @@ class TruncBase(TimezoneMixin, Transform):
             ))
         return copy
 
-    def convert_value(self, value, expression, connection, context):
+    def convert_value(self, value, expression, connection):
         if isinstance(self.output_field, DateTimeField):
             if settings.USE_TZ:
                 if value is None:

+ 15 - 1
django/db/models/sql/compiler.py

@@ -1,5 +1,6 @@
 import collections
 import re
+import warnings
 from itertools import chain
 
 from django.core.exceptions import EmptyResultSet, FieldError
@@ -12,6 +13,8 @@ from django.db.models.sql.constants import (
 from django.db.models.sql.query import Query, get_order_dir
 from django.db.transaction import TransactionManagementError
 from django.db.utils import DatabaseError, NotSupportedError
+from django.utils.deprecation import RemovedInDjango30Warning
+from django.utils.inspect import func_supports_parameter
 
 FORCE = object()
 
@@ -926,7 +929,18 @@ class SQLCompiler:
         for pos, (convs, expression) in converters.items():
             value = row[pos]
             for converter in convs:
-                value = converter(value, expression, self.connection, self.query.context)
+                if func_supports_parameter(converter, 'context'):
+                    warnings.warn(
+                        'Remove the context parameter from %s.%s(). Support for it '
+                        'will be removed in Django 3.0.' % (
+                            converter.__self__.__class__.__name__,
+                            converter.__name__,
+                        ),
+                        RemovedInDjango30Warning,
+                    )
+                    value = converter(value, expression, self.connection, {})
+                else:
+                    value = converter(value, expression, self.connection)
             row[pos] = value
         return tuple(row)
 

+ 2 - 12
django/db/models/sql/query.py

@@ -46,7 +46,7 @@ def get_field_names_from_opts(opts):
 class RawQuery:
     """A single raw SQL query."""
 
-    def __init__(self, sql, using, params=None, context=None):
+    def __init__(self, sql, using, params=None):
         self.params = params or ()
         self.sql = sql
         self.using = using
@@ -57,10 +57,9 @@ class RawQuery:
         self.low_mark, self.high_mark = 0, None  # Used for offset/limit
         self.extra_select = {}
         self.annotation_select = {}
-        self.context = context or {}
 
     def clone(self, using):
-        return RawQuery(self.sql, using, params=self.params, context=self.context.copy())
+        return RawQuery(self.sql, using, params=self.params)
 
     def get_columns(self):
         if self.cursor is None:
@@ -200,8 +199,6 @@ class Query:
         # load.
         self.deferred_loading = (frozenset(), True)
 
-        self.context = {}
-
     @property
     def extra(self):
         if self._extra is None:
@@ -334,15 +331,8 @@ class Query:
         obj.__dict__.update(kwargs)
         if hasattr(obj, '_setup_query'):
             obj._setup_query()
-        obj.context = self.context.copy()
         return obj
 
-    def add_context(self, key, value):
-        self.context[key] = value
-
-    def get_context(self, key, default=None):
-        return self.context.get(key, default)
-
     def relabeled_clone(self, change_map):
         clone = self.clone()
         clone.change_aliases(change_map)

+ 1 - 1
docs/howto/custom-model-fields.txt

@@ -512,7 +512,7 @@ instances::
     class HandField(models.Field):
         # ...
 
-        def from_db_value(self, value, expression, connection, context):
+        def from_db_value(self, value, expression, connection):
             if value is None:
                 return value
             return parse_hand(value)

+ 3 - 0
docs/internals/deprecation.txt

@@ -23,6 +23,9 @@ details on these changes.
 
 * ``HttpRequest.xreadlines()`` will be removed.
 
+* Support for the ``context`` argument of ``Field.from_db_value()`` and
+  ``Expression.convert_value()`` will be removed.
+
 .. _deprecation-removed-in-2.1:
 
 2.1

+ 1 - 1
docs/ref/models/expressions.txt

@@ -714,7 +714,7 @@ calling the appropriate methods on the wrapped expression.
               clone.expression = self.expression.relabeled_clone(change_map)
               return clone
 
-    .. method:: convert_value(value, expression, connection, context)
+    .. method:: convert_value(value, expression, connection)
 
         A hook allowing the expression to coerce ``value`` into a more
         appropriate type.

+ 1 - 1
docs/ref/models/fields.txt

@@ -1797,7 +1797,7 @@ Field API reference
 
     When loading data, :meth:`from_db_value` is used:
 
-    .. method:: from_db_value(value, expression, connection, context)
+    .. method:: from_db_value(value, expression, connection)
 
         Converts a value as returned by the database to a Python object. It is
         the reverse of :meth:`get_prep_value`.

+ 16 - 0
docs/releases/2.0.txt

@@ -564,6 +564,22 @@ Miscellaneous
 Features deprecated in 2.0
 ==========================
 
+``context`` argument of ``Field.from_db_value()`` and ``Expression.convert_value()``
+------------------------------------------------------------------------------------
+
+The ``context`` argument of ``Field.from_db_value()`` and
+``Expression.convert_value()`` is unused as it's always an empty dictionary.
+The signature of both methods is now::
+
+    (self, value, expression, connection)
+
+instead of::
+
+    (self, value, expression, connection, context)
+
+Support for the old signature in custom fields and expressions remains until
+Django 3.0.
+
 Miscellaneous
 -------------
 

+ 1 - 1
tests/custom_pk/fields.py

@@ -40,7 +40,7 @@ class MyAutoField(models.CharField):
             value = MyWrapper(value)
         return value
 
-    def from_db_value(self, value, expression, connection, context):
+    def from_db_value(self, value, expression, connection):
         if not value:
             return
         return MyWrapper(value)

+ 10 - 1
tests/from_db_value/models.py

@@ -17,7 +17,7 @@ class CashField(models.DecimalField):
         kwargs['decimal_places'] = 2
         super().__init__(**kwargs)
 
-    def from_db_value(self, value, expression, connection, context):
+    def from_db_value(self, value, expression, connection):
         cash = Cash(value)
         cash.vendor = connection.vendor
         return cash
@@ -28,3 +28,12 @@ class CashModel(models.Model):
 
     def __str__(self):
         return str(self.cash)
+
+
+class CashFieldDeprecated(CashField):
+    def from_db_value(self, value, expression, connection, context):
+        return super().from_db_value(value, expression, connection)
+
+
+class CashModelDeprecated(models.Model):
+    cash = CashFieldDeprecated()

+ 22 - 0
tests/from_db_value/test_deprecated.py

@@ -0,0 +1,22 @@
+import warnings
+
+from django.test import TestCase
+
+from .models import Cash, CashModelDeprecated
+
+
+class FromDBValueDeprecationTests(TestCase):
+
+    def test_deprecation(self):
+        CashModelDeprecated.objects.create(cash='12.50')
+        with warnings.catch_warnings(record=True) as warns:
+            warnings.simplefilter('always')
+            instance = CashModelDeprecated.objects.get()
+        self.assertIsInstance(instance.cash, Cash)
+        self.assertEqual(len(warns), 1)
+        msg = str(warns[0].message)
+        self.assertEqual(
+            msg,
+            'Remove the context parameter from CashFieldDeprecated.from_db_value(). '
+            'Support for it will be removed in Django 3.0.'
+        )

+ 1 - 1
tests/postgres_tests/models.py

@@ -18,7 +18,7 @@ class Tag:
 
 class TagField(models.SmallIntegerField):
 
-    def from_db_value(self, value, expression, connection, context):
+    def from_db_value(self, value, expression, connection):
         if value is None:
             return value
         return Tag(int(value))

+ 1 - 1
tests/serializers/models/base.py

@@ -124,7 +124,7 @@ class TeamField(models.CharField):
             return value
         return Team(value)
 
-    def from_db_value(self, value, expression, connection, context):
+    def from_db_value(self, value, expression, connection):
         return Team(value)
 
     def value_to_string(self, obj):