Browse Source

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

Unused since a0d166306fbdc41f49e6fadf4ec84b17eb147daa.
Tim Graham 7 years ago
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
     # 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.
     # 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()':
         if value == b'GEOMETRYCOLLECTION()':
             return b'GEOMETRYCOLLECTION EMPTY'
             return b'GEOMETRYCOLLECTION EMPTY'
         return value
         return value

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

@@ -43,7 +43,7 @@ class Extent(GeoAggregate):
     def __init__(self, expression, **extra):
     def __init__(self, expression, **extra):
         super().__init__(expression, output_field=ExtentField(), **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)
         return connection.ops.convert_extent(value)
 
 
 
 
@@ -54,7 +54,7 @@ class Extent3D(GeoAggregate):
     def __init__(self, expression, **extra):
     def __init__(self, expression, **extra):
         super().__init__(expression, output_field=ExtentField(), **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)
         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
             kwargs['geography'] = self.geography
         return name, path, args, kwargs
         return name, path, args, kwargs
 
 
-    def from_db_value(self, value, expression, connection, context):
+    def from_db_value(self, value, expression, connection):
         if value:
         if value:
             value = Geometry(value)
             value = Geometry(value)
             srid = value.srid
             srid = value.srid
@@ -351,7 +351,7 @@ class RasterField(BaseSpatialField):
         self._check_connection(connection)
         self._check_connection(connection)
         return super().db_type(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)
         return connection.ops.parse_raster(value)
 
 
     def contribute_to_class(self, cls, name, **kwargs):
     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 value
         return getattr(value, self.area_att)
         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
         # If the database returns a Decimal, convert it to a float as expected
         # by the Python geometric objects.
         # by the Python geometric objects.
         if isinstance(value, Decimal):
         if isinstance(value, Decimal):
@@ -54,7 +54,7 @@ class DistanceField(models.FloatField):
             raise ValueError('Distance measure is supplied, but units are unknown for result.')
             raise ValueError('Distance measure is supplied, but units are unknown for result.')
         return getattr(value, self.distance_att)
         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:
         if value is None or not self.distance_att:
             return value
             return value
         return Distance(**{self.distance_att: 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):
     def __init__(self, expression, distinct=False, **extra):
         super().__init__(expression, distinct='DISTINCT ' if distinct else '', **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:
         if not value:
             return []
             return []
         return value
         return value
@@ -39,7 +39,7 @@ class JSONBAgg(Aggregate):
     function = 'JSONB_AGG'
     function = 'JSONB_AGG'
     output_field = JSONField()
     output_field = JSONField()
 
 
-    def convert_value(self, value, expression, connection, context):
+    def convert_value(self, value, expression, connection):
         if not value:
         if not value:
             return []
             return []
         return value
         return value
@@ -53,7 +53,7 @@ class StringAgg(Aggregate):
         distinct = 'DISTINCT ' if distinct else ''
         distinct = 'DISTINCT ' if distinct else ''
         super().__init__(expression, delimiter=delimiter, distinct=distinct, **extra)
         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:
         if not value:
             return ''
             return ''
         return value
         return value

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

@@ -41,7 +41,7 @@ class RegrCount(StatAggregate):
     def __init__(self, y, x):
     def __init__(self, y, x):
         super().__init__(y=y, x=x, output_field=IntegerField())
         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:
         if value is None:
             return 0
             return 0
         return int(value)
         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.core import checks, exceptions
 from django.db.models import Field, IntegerField, Transform
 from django.db.models import Field, IntegerField, Transform
 from django.db.models.lookups import Exact, In
 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 django.utils.translation import gettext_lazy as _
 
 
 from ..utils import prefix_validation_error
 from ..utils import prefix_validation_error
@@ -103,11 +104,13 @@ class ArrayField(Field):
             value = [self.base_field.to_python(val) for val in vals]
             value = [self.base_field.to_python(val) for val in vals]
         return value
         return value
 
 
-    def _from_db_value(self, value, expression, connection, context):
+    def _from_db_value(self, value, expression, connection):
         if value is None:
         if value is None:
             return value
             return value
         return [
         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
             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.db import DatabaseError, connections, models, router, transaction
 from django.utils import timezone
 from django.utils import timezone
 from django.utils.encoding import force_bytes
 from django.utils.encoding import force_bytes
+from django.utils.inspect import func_supports_parameter
 
 
 
 
 class Options:
 class Options:
@@ -64,7 +65,10 @@ class DatabaseCache(BaseDatabaseCache):
         expression = models.Expression(output_field=models.DateTimeField())
         expression = models.Expression(output_field=models.DateTimeField())
         for converter in (connection.ops.get_db_converters(expression) +
         for converter in (connection.ops.get_db_converters(expression) +
                           expression.get_db_converters(connection)):
                           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():
         if expires < timezone.now():
             db = router.db_for_write(self.cache_model_class)
             db = router.db_for_write(self.cache_model_class)
@@ -126,7 +130,10 @@ class DatabaseCache(BaseDatabaseCache):
                         expression = models.Expression(output_field=models.DateTimeField())
                         expression = models.Expression(output_field=models.DateTimeField())
                         for converter in (connection.ops.get_db_converters(expression) +
                         for converter in (connection.ops.get_db_converters(expression) +
                                           expression.get_db_converters(connection)):
                                           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)
                     exp = connection.ops.adapt_datetimefield_value(exp)
                     if result and (mode == 'set' or (mode == 'add' and current_expires < now)):
                     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 []
         return []
 
 
-    def convert_durationfield_value(self, value, expression, connection, context):
+    def convert_durationfield_value(self, value, expression, connection):
         if value is not None:
         if value is not None:
             value = str(decimal.Decimal(value) / decimal.Decimal(1000000))
             value = str(decimal.Decimal(value) / decimal.Decimal(1000000))
             value = parse_duration(value)
             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)
             converters.append(self.convert_uuidfield_value)
         return converters
         return converters
 
 
-    def convert_textfield_value(self, value, expression, connection, context):
+    def convert_textfield_value(self, value, expression, connection):
         if value is not None:
         if value is not None:
             value = force_text(value)
             value = force_text(value)
         return 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):
         if value in (0, 1):
             value = bool(value)
             value = bool(value)
         return 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 value is not None:
             if settings.USE_TZ:
             if settings.USE_TZ:
                 value = timezone.make_aware(value, self.connection.timezone)
                 value = timezone.make_aware(value, self.connection.timezone)
         return 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:
         if value is not None:
             value = uuid.UUID(value)
             value = uuid.UUID(value)
         return value
         return value

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

@@ -165,17 +165,17 @@ END;
         converters.append(self.convert_empty_values)
         converters.append(self.convert_empty_values)
         return converters
         return converters
 
 
-    def convert_textfield_value(self, value, expression, connection, context):
+    def convert_textfield_value(self, value, expression, connection):
         if isinstance(value, Database.LOB):
         if isinstance(value, Database.LOB):
             value = value.read()
             value = value.read()
         return value
         return value
 
 
-    def convert_binaryfield_value(self, value, expression, connection, context):
+    def convert_binaryfield_value(self, value, expression, connection):
         if isinstance(value, Database.LOB):
         if isinstance(value, Database.LOB):
             value = force_bytes(value.read())
             value = force_bytes(value.read())
         return 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):
         if value in (0, 1):
             value = bool(value)
             value = bool(value)
         return value
         return value
@@ -184,28 +184,28 @@ END;
     # DATE and TIMESTAMP columns, but Django wants to see a
     # DATE and TIMESTAMP columns, but Django wants to see a
     # python datetime.date, .time, or .datetime.
     # 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 value is not None:
             if settings.USE_TZ:
             if settings.USE_TZ:
                 value = timezone.make_aware(value, self.connection.timezone)
                 value = timezone.make_aware(value, self.connection.timezone)
         return value
         return value
 
 
-    def convert_datefield_value(self, value, expression, connection, context):
+    def convert_datefield_value(self, value, expression, connection):
         if isinstance(value, Database.Timestamp):
         if isinstance(value, Database.Timestamp):
             value = value.date()
             value = value.date()
         return value
         return value
 
 
-    def convert_timefield_value(self, value, expression, connection, context):
+    def convert_timefield_value(self, value, expression, connection):
         if isinstance(value, Database.Timestamp):
         if isinstance(value, Database.Timestamp):
             value = value.time()
             value = value.time()
         return 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:
         if value is not None:
             value = uuid.UUID(value)
             value = uuid.UUID(value)
         return 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
         # Oracle stores empty strings as null. We need to undo this in
         # order to adhere to the Django convention of using the empty
         # order to adhere to the Django convention of using the empty
         # string instead of null, but only if the field accepts the
         # 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)
             converters.append(self.convert_booleanfield_value)
         return converters
         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 value is not None:
             if not isinstance(value, datetime.datetime):
             if not isinstance(value, datetime.datetime):
                 value = parse_datetime(value)
                 value = parse_datetime(value)
@@ -220,30 +220,30 @@ class DatabaseOperations(BaseDatabaseOperations):
                 value = timezone.make_aware(value, self.connection.timezone)
                 value = timezone.make_aware(value, self.connection.timezone)
         return value
         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 value is not None:
             if not isinstance(value, datetime.date):
             if not isinstance(value, datetime.date):
                 value = parse_date(value)
                 value = parse_date(value)
         return 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 value is not None:
             if not isinstance(value, datetime.time):
             if not isinstance(value, datetime.time):
                 value = parse_time(value)
                 value = parse_time(value)
         return 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:
         if value is not None:
             value = expression.output_field.format_number(value)
             value = expression.output_field.format_number(value)
             value = backend_utils.typecast_decimal(value)
             value = backend_utils.typecast_decimal(value)
         return 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:
         if value is not None:
             value = uuid.UUID(value)
             value = uuid.UUID(value)
         return 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
         return bool(value) if value in (1, 0) else value
 
 
     def bulk_insert_sql(self, fields, placeholder_rows):
     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):
     def _get_repr_options(self):
         return {'distinct': self.extra['distinct'] != ''}
         return {'distinct': self.extra['distinct'] != ''}
 
 
-    def convert_value(self, value, expression, connection, context):
+    def convert_value(self, value, expression, connection):
         if value is None:
         if value is None:
             return 0
             return 0
         return int(value)
         return int(value)
@@ -99,7 +99,7 @@ class StdDev(Aggregate):
     def _get_repr_options(self):
     def _get_repr_options(self):
         return {'sample': self.function == 'STDDEV_SAMP'}
         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:
         if value is None:
             return value
             return value
         return float(value)
         return float(value)
@@ -129,7 +129,7 @@ class Variance(Aggregate):
     def _get_repr_options(self):
     def _get_repr_options(self):
         return {'sample': self.function == 'VAR_SAMP'}
         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:
         if value is None:
             return value
             return value
         return float(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.')
                 raise FieldError('Expression contains mixed types. You must set output_field.')
             return 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
         Expressions provide their own converters because users have the option
         of manually specifying the output_field which may be a different type
         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):
     def db_parameters(self, connection):
         return {"type": self.db_type(connection), "check": self.db_check(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):
         if (not value) and isinstance(value, str):
             return None
             return None
         return value
         return value

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

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

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

@@ -1,5 +1,6 @@
 import collections
 import collections
 import re
 import re
+import warnings
 from itertools import chain
 from itertools import chain
 
 
 from django.core.exceptions import EmptyResultSet, FieldError
 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.models.sql.query import Query, get_order_dir
 from django.db.transaction import TransactionManagementError
 from django.db.transaction import TransactionManagementError
 from django.db.utils import DatabaseError, NotSupportedError
 from django.db.utils import DatabaseError, NotSupportedError
+from django.utils.deprecation import RemovedInDjango30Warning
+from django.utils.inspect import func_supports_parameter
 
 
 FORCE = object()
 FORCE = object()
 
 
@@ -926,7 +929,18 @@ class SQLCompiler:
         for pos, (convs, expression) in converters.items():
         for pos, (convs, expression) in converters.items():
             value = row[pos]
             value = row[pos]
             for converter in convs:
             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
             row[pos] = value
         return tuple(row)
         return tuple(row)
 
 

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

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

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

@@ -512,7 +512,7 @@ instances::
     class HandField(models.Field):
     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:
             if value is None:
                 return value
                 return value
             return parse_hand(value)
             return parse_hand(value)

+ 3 - 0
docs/internals/deprecation.txt

@@ -23,6 +23,9 @@ details on these changes.
 
 
 * ``HttpRequest.xreadlines()`` will be removed.
 * ``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:
 .. _deprecation-removed-in-2.1:
 
 
 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)
               clone.expression = self.expression.relabeled_clone(change_map)
               return clone
               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
         A hook allowing the expression to coerce ``value`` into a more
         appropriate type.
         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:
     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
         Converts a value as returned by the database to a Python object. It is
         the reverse of :meth:`get_prep_value`.
         the reverse of :meth:`get_prep_value`.

+ 16 - 0
docs/releases/2.0.txt

@@ -564,6 +564,22 @@ Miscellaneous
 Features deprecated in 2.0
 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
 Miscellaneous
 -------------
 -------------
 
 

+ 1 - 1
tests/custom_pk/fields.py

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

+ 10 - 1
tests/from_db_value/models.py

@@ -17,7 +17,7 @@ class CashField(models.DecimalField):
         kwargs['decimal_places'] = 2
         kwargs['decimal_places'] = 2
         super().__init__(**kwargs)
         super().__init__(**kwargs)
 
 
-    def from_db_value(self, value, expression, connection, context):
+    def from_db_value(self, value, expression, connection):
         cash = Cash(value)
         cash = Cash(value)
         cash.vendor = connection.vendor
         cash.vendor = connection.vendor
         return cash
         return cash
@@ -28,3 +28,12 @@ class CashModel(models.Model):
 
 
     def __str__(self):
     def __str__(self):
         return str(self.cash)
         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):
 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:
         if value is None:
             return value
             return value
         return Tag(int(value))
         return Tag(int(value))

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

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