summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2016-04-23 19:13:31 +0200
committerClaude Paroz <claude@2xlibre.net>2016-05-04 20:02:01 +0200
commit388bb5bd9aa3cd43825cd8a3632a57d8204f875f (patch)
treeae1d5c1d06234b79ec3a668df2e4926f73d30ce7
parent1206eea11e506c4e740ba2f0c1feaa01452d804b (diff)
downloaddjango-388bb5bd9aa3cd43825cd8a3632a57d8204f875f.tar.gz
Fixed #22936 -- Obsoleted Field.get_prep_lookup()/get_db_prep_lookup()
Thanks Tim Graham for completing the initial patch.
-rw-r--r--django/contrib/admin/filters.py3
-rw-r--r--django/contrib/admin/widgets.py1
-rw-r--r--django/contrib/gis/db/models/fields.py7
-rw-r--r--django/contrib/postgres/fields/jsonb.py7
-rw-r--r--django/contrib/postgres/fields/ranges.py2
-rw-r--r--django/contrib/postgres/lookups.py7
-rw-r--r--django/db/models/expressions.py2
-rw-r--r--django/db/models/fields/__init__.py58
-rw-r--r--django/db/models/fields/files.py5
-rw-r--r--django/db/models/fields/related_lookups.py14
-rw-r--r--django/db/models/fields/reverse_related.py7
-rw-r--r--django/db/models/lookups.py33
-rw-r--r--docs/howto/custom-model-fields.txt61
-rw-r--r--docs/ref/models/fields.txt23
-rw-r--r--docs/releases/1.10.txt19
-rw-r--r--tests/model_fields/test_booleanfield.py26
-rw-r--r--tests/model_fields/test_custom_fields.py22
-rw-r--r--tests/model_fields/test_decimalfield.py7
-rw-r--r--tests/queries/tests.py5
19 files changed, 89 insertions, 220 deletions
diff --git a/django/contrib/admin/filters.py b/django/contrib/admin/filters.py
index bb9731bdfc..f986bca713 100644
--- a/django/contrib/admin/filters.py
+++ b/django/contrib/admin/filters.py
@@ -232,6 +232,9 @@ class BooleanFieldListFilter(FieldListFilter):
self.lookup_val = request.GET.get(self.lookup_kwarg)
self.lookup_val2 = request.GET.get(self.lookup_kwarg2)
super(BooleanFieldListFilter, self).__init__(field, request, params, model, model_admin, field_path)
+ if (self.used_parameters and self.lookup_kwarg in self.used_parameters and
+ self.used_parameters[self.lookup_kwarg] in ('1', '0')):
+ self.used_parameters[self.lookup_kwarg] = bool(int(self.used_parameters[self.lookup_kwarg]))
def expected_parameters(self):
return [self.lookup_kwarg, self.lookup_kwarg2]
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
index 8967200318..e3553de70a 100644
--- a/django/contrib/admin/widgets.py
+++ b/django/contrib/admin/widgets.py
@@ -128,7 +128,6 @@ def url_params_from_lookup_dict(lookups):
if isinstance(v, (tuple, list)):
v = ','.join(str(x) for x in v)
elif isinstance(v, bool):
- # See django.db.fields.BooleanField.get_prep_lookup
v = ('0', '1')[v]
else:
v = six.text_type(v)
diff --git a/django/contrib/gis/db/models/fields.py b/django/contrib/gis/db/models/fields.py
index 4fa9674419..8452a11c5f 100644
--- a/django/contrib/gis/db/models/fields.py
+++ b/django/contrib/gis/db/models/fields.py
@@ -316,13 +316,6 @@ class GeometryField(GeoSelectFormatMixin, BaseSpatialField):
params = [connection.ops.Adapter(value)]
return params
- def get_prep_lookup(self, lookup_type, value):
- if lookup_type == 'contains':
- # 'contains' name might conflict with the "normal" contains lookup,
- # for which the value is not prepared, but left as-is.
- return self.get_prep_value(value)
- return super(GeometryField, self).get_prep_lookup(lookup_type, value)
-
def get_db_prep_save(self, value, connection):
"Prepares the value for saving in the database."
if not value:
diff --git a/django/contrib/postgres/fields/jsonb.py b/django/contrib/postgres/fields/jsonb.py
index d580ea2efd..ae83d9e379 100644
--- a/django/contrib/postgres/fields/jsonb.py
+++ b/django/contrib/postgres/fields/jsonb.py
@@ -31,13 +31,6 @@ class JSONField(Field):
return Json(value)
return value
- def get_prep_lookup(self, lookup_type, value):
- if lookup_type in ('has_key', 'has_keys', 'has_any_keys'):
- return value
- if isinstance(value, (dict, list)):
- return Json(value)
- return super(JSONField, self).get_prep_lookup(lookup_type, value)
-
def validate(self, value, model_instance):
super(JSONField, self).validate(value, model_instance)
try:
diff --git a/django/contrib/postgres/fields/ranges.py b/django/contrib/postgres/fields/ranges.py
index 3816de84f1..ab7708d288 100644
--- a/django/contrib/postgres/fields/ranges.py
+++ b/django/contrib/postgres/fields/ranges.py
@@ -154,7 +154,7 @@ class RangeContainedBy(models.Lookup):
return sql % (lhs, rhs), params
def get_prep_lookup(self):
- return RangeField().get_prep_lookup(self.lookup_name, self.rhs)
+ return RangeField().get_prep_value(self.rhs)
models.DateField.register_lookup(RangeContainedBy)
diff --git a/django/contrib/postgres/lookups.py b/django/contrib/postgres/lookups.py
index 1a71725678..a39c9679e7 100644
--- a/django/contrib/postgres/lookups.py
+++ b/django/contrib/postgres/lookups.py
@@ -1,4 +1,5 @@
from django.db.models import Lookup, Transform
+from django.utils.encoding import force_text
from .search import SearchVector, SearchVectorExact, SearchVectorField
@@ -29,14 +30,18 @@ class Overlap(PostgresSimpleLookup):
class HasKey(PostgresSimpleLookup):
lookup_name = 'has_key'
operator = '?'
+ prepare_rhs = False
class HasKeys(PostgresSimpleLookup):
lookup_name = 'has_keys'
operator = '?&'
+ def get_prep_lookup(self):
+ return [force_text(item) for item in self.rhs]
-class HasAnyKeys(PostgresSimpleLookup):
+
+class HasAnyKeys(HasKeys):
lookup_name = 'has_any_keys'
operator = '?|'
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py
index b1133e24ea..df82d2ec58 100644
--- a/django/db/models/expressions.py
+++ b/django/db/models/expressions.py
@@ -213,7 +213,7 @@ class BaseExpression(object):
def _prepare(self, field):
"""
- Hook used by Field.get_prep_lookup() to do custom preparation.
+ Hook used by Lookup.get_prep_lookup() to do custom preparation.
"""
return self
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 5951681e81..e567e12250 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -741,8 +741,7 @@ class Field(RegisterLookupMixin):
"""Returns field's value prepared for interacting with the database
backend.
- Used by the default implementations of ``get_db_prep_save``and
- `get_db_prep_lookup```
+ Used by the default implementations of get_db_prep_save().
"""
if not prepared:
value = self.get_prep_value(value)
@@ -755,36 +754,6 @@ class Field(RegisterLookupMixin):
return self.get_db_prep_value(value, connection=connection,
prepared=False)
- def get_prep_lookup(self, lookup_type, value):
- """
- Perform preliminary non-db specific lookup checks and conversions
- """
- if hasattr(value, '_prepare'):
- return value._prepare(self)
-
- if lookup_type in {
- 'iexact', 'contains', 'icontains',
- 'startswith', 'istartswith', 'endswith', 'iendswith',
- 'isnull', 'search', 'regex', 'iregex',
- }:
- return value
- elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'):
- return self.get_prep_value(value)
- elif lookup_type in ('range', 'in'):
- return [self.get_prep_value(v) for v in value]
- return self.get_prep_value(value)
-
- def get_db_prep_lookup(self, lookup_type, value, connection,
- prepared=False):
- """
- Returns field's value prepared for database lookup.
- """
- if not prepared:
- value = self.get_prep_lookup(lookup_type, value)
- prepared = True
-
- return [value]
-
def has_default(self):
"""
Returns a boolean of whether this field has a default value.
@@ -1049,20 +1018,11 @@ class BooleanField(Field):
params={'value': value},
)
- def get_prep_lookup(self, lookup_type, value):
- # Special-case handling for filters coming from a Web request (e.g. the
- # admin interface). Only works for scalar values (not lists). If you're
- # passing in a list, you might as well make things the right type when
- # constructing the list.
- if value in ('1', '0'):
- value = bool(int(value))
- return super(BooleanField, self).get_prep_lookup(lookup_type, value)
-
def get_prep_value(self, value):
value = super(BooleanField, self).get_prep_value(value)
if value is None:
return None
- return bool(value)
+ return self.to_python(value)
def formfield(self, **kwargs):
# Unlike most fields, BooleanField figures out include_blank from
@@ -1453,8 +1413,6 @@ class DateTimeField(DateField):
# contribute_to_class is inherited from DateField, it registers
# get_next_by_FOO and get_prev_by_FOO
- # get_prep_lookup is inherited from DateField
-
def get_prep_value(self, value):
value = super(DateTimeField, self).get_prep_value(value)
value = self.to_python(value)
@@ -2051,21 +2009,11 @@ class NullBooleanField(Field):
params={'value': value},
)
- def get_prep_lookup(self, lookup_type, value):
- # Special-case handling for filters coming from a Web request (e.g. the
- # admin interface). Only works for scalar values (not lists). If you're
- # passing in a list, you might as well make things the right type when
- # constructing the list.
- if value in ('1', '0'):
- value = bool(int(value))
- return super(NullBooleanField, self).get_prep_lookup(lookup_type,
- value)
-
def get_prep_value(self, value):
value = super(NullBooleanField, self).get_prep_value(value)
if value is None:
return None
- return bool(value)
+ return self.to_python(value)
def formfield(self, **kwargs):
defaults = {
diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py
index 3ba2c14325..f18e2f39b3 100644
--- a/django/db/models/fields/files.py
+++ b/django/db/models/fields/files.py
@@ -271,11 +271,6 @@ class FileField(Field):
def get_internal_type(self):
return "FileField"
- def get_prep_lookup(self, lookup_type, value):
- if hasattr(value, 'name'):
- value = value.name
- return super(FileField, self).get_prep_lookup(lookup_type, value)
-
def get_prep_value(self, value):
"Returns field's value prepared for saving into a database."
value = super(FileField, self).get_prep_value(value)
diff --git a/django/db/models/fields/related_lookups.py b/django/db/models/fields/related_lookups.py
index 0f5a8b5e21..3123e94073 100644
--- a/django/db/models/fields/related_lookups.py
+++ b/django/db/models/fields/related_lookups.py
@@ -44,15 +44,15 @@ class RelatedIn(In):
if not isinstance(self.lhs, MultiColSource) and self.rhs_is_direct_value():
# If we get here, we are dealing with single-column relations.
self.rhs = [get_normalized_value(val, self.lhs)[0] for val in self.rhs]
- # We need to run the related field's get_prep_lookup(). Consider case
+ # We need to run the related field's get_prep_value(). Consider case
# ForeignKey to IntegerField given value 'abc'. The ForeignKey itself
# doesn't have validation for non-integers, so we must run validation
# using the target field.
if hasattr(self.lhs.output_field, 'get_path_info'):
- # Run the target field's get_prep_lookup. We can safely assume there is
+ # Run the target field's get_prep_value. We can safely assume there is
# only one as we don't get to the direct value branch otherwise.
- self.rhs = self.lhs.output_field.get_path_info()[-1].target_fields[-1].get_prep_lookup(
- self.lookup_name, self.rhs)
+ target_field = self.lhs.output_field.get_path_info()[-1].target_fields[-1]
+ self.rhs = [target_field.get_prep_value(v) for v in self.rhs]
return super(RelatedIn, self).get_prep_lookup()
def as_sql(self, compiler, connection):
@@ -88,15 +88,15 @@ class RelatedLookupMixin(object):
if not isinstance(self.lhs, MultiColSource) and self.rhs_is_direct_value():
# If we get here, we are dealing with single-column relations.
self.rhs = get_normalized_value(self.rhs, self.lhs)[0]
- # We need to run the related field's get_prep_lookup(). Consider case
+ # We need to run the related field's get_prep_value(). Consider case
# ForeignKey to IntegerField given value 'abc'. The ForeignKey itself
# doesn't have validation for non-integers, so we must run validation
# using the target field.
if hasattr(self.lhs.output_field, 'get_path_info'):
# Get the target field. We can safely assume there is only one
# as we don't get to the direct value branch otherwise.
- self.rhs = self.lhs.output_field.get_path_info()[-1].target_fields[-1].get_prep_lookup(
- self.lookup_name, self.rhs)
+ target_field = self.lhs.output_field.get_path_info()[-1].target_fields[-1]
+ self.rhs = target_field.get_prep_value(self.rhs)
return super(RelatedLookupMixin, self).get_prep_lookup()
diff --git a/django/db/models/fields/reverse_related.py b/django/db/models/fields/reverse_related.py
index c477ab5a93..1554261246 100644
--- a/django/db/models/fields/reverse_related.py
+++ b/django/db/models/fields/reverse_related.py
@@ -110,9 +110,6 @@ class ForeignObjectRel(object):
def one_to_one(self):
return self.field.one_to_one
- def get_prep_lookup(self, lookup_name, value):
- return self.field.get_prep_lookup(lookup_name, value)
-
def get_lookup(self, lookup_name):
return self.field.get_lookup(lookup_name)
@@ -142,10 +139,6 @@ class ForeignObjectRel(object):
(x._get_pk_val(), smart_text(x)) for x in self.related_model._default_manager.all()
]
- def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False):
- # Defer to the actual field definition for db prep
- return self.field.get_db_prep_lookup(lookup_type, value, connection=connection, prepared=prepared)
-
def is_hidden(self):
"Should the related object be hidden?"
return bool(self.related_name) and self.related_name[-1] == '+'
diff --git a/django/db/models/lookups.py b/django/db/models/lookups.py
index 62acfcb887..9f398b2b1a 100644
--- a/django/db/models/lookups.py
+++ b/django/db/models/lookups.py
@@ -16,6 +16,7 @@ from django.utils.six.moves import range
class Lookup(object):
lookup_name = None
+ prepare_rhs = True
def __init__(self, lhs, rhs):
self.lhs, self.rhs = lhs, rhs
@@ -56,12 +57,14 @@ class Lookup(object):
return sqls, sqls_params
def get_prep_lookup(self):
- return self.lhs.output_field.get_prep_lookup(self.lookup_name, self.rhs)
+ if hasattr(self.rhs, '_prepare'):
+ return self.rhs._prepare(self.lhs.output_field)
+ if self.prepare_rhs and hasattr(self.lhs.output_field, 'get_prep_value'):
+ return self.lhs.output_field.get_prep_value(self.rhs)
+ return self.rhs
def get_db_prep_lookup(self, value, connection):
- return (
- '%s', self.lhs.output_field.get_db_prep_lookup(
- self.lookup_name, value, connection, prepared=True))
+ return ('%s', [value])
def process_lhs(self, compiler, connection, lhs=None):
lhs = lhs or self.lhs
@@ -199,6 +202,7 @@ Field.register_lookup(Exact)
class IExact(BuiltinLookup):
lookup_name = 'iexact'
+ prepare_rhs = False
def process_rhs(self, qn, connection):
rhs, params = super(IExact, self).process_rhs(qn, connection)
@@ -254,6 +258,13 @@ IntegerField.register_lookup(IntegerLessThan)
class In(FieldGetDbPrepValueIterableMixin, BuiltinLookup):
lookup_name = 'in'
+ def get_prep_lookup(self):
+ if hasattr(self.rhs, '_prepare'):
+ return self.rhs._prepare(self.lhs.output_field)
+ if hasattr(self.lhs.output_field, 'get_prep_value'):
+ return [self.lhs.output_field.get_prep_value(v) for v in self.rhs]
+ return self.rhs
+
def process_rhs(self, compiler, connection):
db_rhs = getattr(self.rhs, '_db', None)
if db_rhs is not None and db_rhs != connection.alias:
@@ -335,6 +346,7 @@ class PatternLookup(BuiltinLookup):
class Contains(PatternLookup):
lookup_name = 'contains'
+ prepare_rhs = False
def process_rhs(self, qn, connection):
rhs, params = super(Contains, self).process_rhs(qn, connection)
@@ -346,11 +358,13 @@ Field.register_lookup(Contains)
class IContains(Contains):
lookup_name = 'icontains'
+ prepare_rhs = False
Field.register_lookup(IContains)
class StartsWith(PatternLookup):
lookup_name = 'startswith'
+ prepare_rhs = False
def process_rhs(self, qn, connection):
rhs, params = super(StartsWith, self).process_rhs(qn, connection)
@@ -362,6 +376,7 @@ Field.register_lookup(StartsWith)
class IStartsWith(PatternLookup):
lookup_name = 'istartswith'
+ prepare_rhs = False
def process_rhs(self, qn, connection):
rhs, params = super(IStartsWith, self).process_rhs(qn, connection)
@@ -373,6 +388,7 @@ Field.register_lookup(IStartsWith)
class EndsWith(PatternLookup):
lookup_name = 'endswith'
+ prepare_rhs = False
def process_rhs(self, qn, connection):
rhs, params = super(EndsWith, self).process_rhs(qn, connection)
@@ -384,6 +400,7 @@ Field.register_lookup(EndsWith)
class IEndsWith(PatternLookup):
lookup_name = 'iendswith'
+ prepare_rhs = False
def process_rhs(self, qn, connection):
rhs, params = super(IEndsWith, self).process_rhs(qn, connection)
@@ -396,6 +413,11 @@ Field.register_lookup(IEndsWith)
class Range(FieldGetDbPrepValueIterableMixin, BuiltinLookup):
lookup_name = 'range'
+ def get_prep_lookup(self):
+ if hasattr(self.rhs, '_prepare'):
+ return self.rhs._prepare(self.lhs.output_field)
+ return [self.lhs.output_field.get_prep_value(v) for v in self.rhs]
+
def get_rhs_op(self, connection, rhs):
return "BETWEEN %s AND %s" % (rhs[0], rhs[1])
@@ -411,6 +433,7 @@ Field.register_lookup(Range)
class IsNull(BuiltinLookup):
lookup_name = 'isnull'
+ prepare_rhs = False
def as_sql(self, compiler, connection):
sql, params = compiler.compile(self.lhs)
@@ -423,6 +446,7 @@ Field.register_lookup(IsNull)
class Search(BuiltinLookup):
lookup_name = 'search'
+ prepare_rhs = False
def as_sql(self, compiler, connection):
warnings.warn(
@@ -438,6 +462,7 @@ Field.register_lookup(Search)
class Regex(BuiltinLookup):
lookup_name = 'regex'
+ prepare_rhs = False
def as_sql(self, compiler, connection):
if self.lookup_name in connection.operators:
diff --git a/docs/howto/custom-model-fields.txt b/docs/howto/custom-model-fields.txt
index 99dd1e217a..1730419787 100644
--- a/docs/howto/custom-model-fields.txt
+++ b/docs/howto/custom-model-fields.txt
@@ -577,67 +577,6 @@ the end. You should also update the model's attribute if you make any changes
to the value so that code holding references to the model will always see the
correct value.
-.. _preparing-values-for-use-in-database-lookups:
-
-Preparing values for use in database lookups
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-As with value conversions, preparing a value for database lookups is a
-two phase process.
-
-:meth:`.get_prep_lookup` performs the first phase of lookup preparation:
-type conversion and data validation.
-
-Prepares the ``value`` for passing to the database when used in a lookup (a
-``WHERE`` constraint in SQL). The ``lookup_type`` parameter will be one of the
-valid Django filter lookups: ``exact``, ``iexact``, ``contains``, ``icontains``,
-``gt``, ``gte``, ``lt``, ``lte``, ``in``, ``startswith``, ``istartswith``,
-``endswith``, ``iendswith``, ``range``, ``year``, ``month``, ``day``,
-``isnull``, ``search``, ``regex``, and ``iregex``.
-
-If you are using :doc:`custom lookups </howto/custom-lookups>`, the
-``lookup_type`` can be any ``lookup_name`` used by the project's custom lookups.
-
-Your method must be prepared to handle all of these ``lookup_type`` values and
-should raise either a ``ValueError`` if the ``value`` is of the wrong sort (a
-list when you were expecting an object, for example) or a ``TypeError`` if
-your field does not support that type of lookup. For many fields, you can get
-by with handling the lookup types that need special handling for your field
-and pass the rest to the :meth:`~Field.get_db_prep_lookup` method of the parent
-class.
-
-If you needed to implement :meth:`.get_db_prep_save`, you will usually need to
-implement :meth:`.get_prep_lookup`. If you don't, :meth:`.get_prep_value` will
-be called by the default implementation, to manage ``exact``, ``gt``, ``gte``,
-``lt``, ``lte``, ``in`` and ``range`` lookups.
-
-You may also want to implement this method to limit the lookup types that could
-be used with your custom field type.
-
-Note that, for ``"range"`` and ``"in"`` lookups, ``get_prep_lookup`` will receive
-a list of objects (presumably of the right type) and will need to convert them
-to a list of things of the right type for passing to the database. Most of the
-time, you can reuse ``get_prep_value()``, or at least factor out some common
-pieces.
-
-For example, the following code implements ``get_prep_lookup`` to limit the
-accepted lookup types to ``exact`` and ``in``::
-
- class HandField(models.Field):
- # ...
-
- def get_prep_lookup(self, lookup_type, value):
- # We only handle 'exact' and 'in'. All others are errors.
- if lookup_type == 'exact':
- return self.get_prep_value(value)
- elif lookup_type == 'in':
- return [self.get_prep_value(v) for v in value]
- else:
- raise TypeError('Lookup type %r not supported.' % lookup_type)
-
-For performing database-specific data conversions required by a lookup,
-you can override :meth:`~Field.get_db_prep_lookup`.
-
.. _specifying-form-field-for-model-field:
Specifying the form field for a model field
diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt
index 3ecc213892..5bd1a148b8 100644
--- a/docs/ref/models/fields.txt
+++ b/docs/ref/models/fields.txt
@@ -1717,8 +1717,7 @@ Field API reference
``Field`` is an abstract class that represents a database table column.
Django uses fields to create the database table (:meth:`db_type`), to map
Python types to database (:meth:`get_prep_value`) and vice-versa
- (:meth:`from_db_value`), and to apply :doc:`/ref/models/lookups`
- (:meth:`get_prep_lookup`).
+ (:meth:`from_db_value`).
A field is thus a fundamental piece in different Django APIs, notably,
:class:`models <django.db.models.Model>` and :class:`querysets
@@ -1847,26 +1846,6 @@ Field API reference
See :ref:`preprocessing-values-before-saving` for usage.
- When a lookup is used on a field, the value may need to be "prepared".
- Django exposes two methods for this:
-
- .. method:: get_prep_lookup(lookup_type, value)
-
- Prepares ``value`` to the database prior to be used in a lookup.
- The ``lookup_type`` will be the registered name of the lookup. For
- example: ``"exact"``, ``"iexact"``, or ``"contains"``.
-
- See :ref:`preparing-values-for-use-in-database-lookups` for usage.
-
- .. method:: get_db_prep_lookup(lookup_type, value, connection, prepared=False)
-
- Similar to :meth:`get_db_prep_value`, but for performing a lookup.
-
- As with :meth:`get_db_prep_value`, the specific connection that will
- be used for the query is passed as ``connection``. In addition,
- ``prepared`` describes whether the value has already been prepared with
- :meth:`get_prep_lookup`.
-
Fields often receive their values as a different type, either from
serialization or from forms.
diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt
index 826752996b..e750fb1d47 100644
--- a/docs/releases/1.10.txt
+++ b/docs/releases/1.10.txt
@@ -677,6 +677,25 @@ You can check if your database has any of the removed hashers like this::
# Unsalted MD5 passwords might not have an 'md5$$' prefix:
User.objects.filter(password__length=32)
+``Field.get_prep_lookup()`` and ``Field.get_db_prep_lookup()`` methods are removed
+----------------------------------------------------------------------------------
+
+If you have a custom field that implements either of these methods, register a
+custom lookup for it. For example::
+
+ from django.db.models import Field
+ from django.db.models.lookups import Exact
+
+ class MyField(Field):
+ ...
+
+ class MyFieldExact(Exact):
+ def get_prep_lookup(self):
+ # do_custom_stuff_for_myfield
+ ....
+
+ MyField.register_lookup(MyFieldExact)
+
:mod:`django.contrib.gis`
-------------------------
diff --git a/tests/model_fields/test_booleanfield.py b/tests/model_fields/test_booleanfield.py
index 2975f4723c..9c90bb5709 100644
--- a/tests/model_fields/test_booleanfield.py
+++ b/tests/model_fields/test_booleanfield.py
@@ -1,29 +1,29 @@
from django.core.exceptions import ValidationError
-from django.db import IntegrityError, connection, models, transaction
+from django.db import IntegrityError, models, transaction
from django.test import SimpleTestCase, TestCase
from .models import BooleanModel, FksToBooleans, NullBooleanModel
class BooleanFieldTests(TestCase):
- def _test_get_db_prep_lookup(self, f):
- self.assertEqual(f.get_db_prep_lookup('exact', True, connection=connection), [True])
- self.assertEqual(f.get_db_prep_lookup('exact', '1', connection=connection), [True])
- self.assertEqual(f.get_db_prep_lookup('exact', 1, connection=connection), [True])
- self.assertEqual(f.get_db_prep_lookup('exact', False, connection=connection), [False])
- self.assertEqual(f.get_db_prep_lookup('exact', '0', connection=connection), [False])
- self.assertEqual(f.get_db_prep_lookup('exact', 0, connection=connection), [False])
- self.assertEqual(f.get_db_prep_lookup('exact', None, connection=connection), [None])
+ def _test_get_prep_value(self, f):
+ self.assertEqual(f.get_prep_value(True), True)
+ self.assertEqual(f.get_prep_value('1'), True)
+ self.assertEqual(f.get_prep_value(1), True)
+ self.assertEqual(f.get_prep_value(False), False)
+ self.assertEqual(f.get_prep_value('0'), False)
+ self.assertEqual(f.get_prep_value(0), False)
+ self.assertEqual(f.get_prep_value(None), None)
def _test_to_python(self, f):
self.assertIs(f.to_python(1), True)
self.assertIs(f.to_python(0), False)
- def test_booleanfield_get_db_prep_lookup(self):
- self._test_get_db_prep_lookup(models.BooleanField())
+ def test_booleanfield_get_prep_value(self):
+ self._test_get_prep_value(models.BooleanField())
- def test_nullbooleanfield_get_db_prep_lookup(self):
- self._test_get_db_prep_lookup(models.NullBooleanField())
+ def test_nullbooleanfield_get_prep_value(self):
+ self._test_get_prep_value(models.NullBooleanField())
def test_booleanfield_to_python(self):
self._test_to_python(models.BooleanField())
diff --git a/tests/model_fields/test_custom_fields.py b/tests/model_fields/test_custom_fields.py
deleted file mode 100644
index c41e19416e..0000000000
--- a/tests/model_fields/test_custom_fields.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from django.db import connection, models
-from django.test import SimpleTestCase
-
-
-class CustomFieldTests(SimpleTestCase):
-
- def test_get_prep_value_count(self):
- """
- Field values are not prepared twice in get_db_prep_lookup() (#14786).
- """
- class NoopField(models.TextField):
- def __init__(self, *args, **kwargs):
- self.prep_value_count = 0
- super(NoopField, self).__init__(*args, **kwargs)
-
- def get_prep_value(self, value):
- self.prep_value_count += 1
- return super(NoopField, self).get_prep_value(value)
-
- field = NoopField()
- field.get_db_prep_lookup('exact', 'TEST', connection=connection, prepared=False)
- self.assertEqual(field.prep_value_count, 1)
diff --git a/tests/model_fields/test_decimalfield.py b/tests/model_fields/test_decimalfield.py
index af9bd6c4be..0414581816 100644
--- a/tests/model_fields/test_decimalfield.py
+++ b/tests/model_fields/test_decimalfield.py
@@ -2,7 +2,7 @@ from decimal import Decimal
from django.core import validators
from django.core.exceptions import ValidationError
-from django.db import connection, models
+from django.db import models
from django.test import TestCase
from .models import BigD, Foo
@@ -27,9 +27,10 @@ class DecimalFieldTests(TestCase):
self.assertEqual(f._format(f.to_python('2.6')), '2.6')
self.assertEqual(f._format(None), None)
- def test_get_db_prep_lookup(self):
+ def test_get_prep_value(self):
f = models.DecimalField(max_digits=5, decimal_places=1)
- self.assertEqual(f.get_db_prep_lookup('exact', None, connection=connection), [None])
+ self.assertEqual(f.get_prep_value(None), None)
+ self.assertEqual(f.get_prep_value('2.4'), Decimal('2.4'))
def test_filter_with_strings(self):
"""
diff --git a/tests/queries/tests.py b/tests/queries/tests.py
index 098f47b8d1..6af66ba7f9 100644
--- a/tests/queries/tests.py
+++ b/tests/queries/tests.py
@@ -1229,9 +1229,8 @@ class Queries2Tests(TestCase):
)
def test_ticket12239(self):
- # Float was being rounded to integer on gte queries on integer field. Tests
- # show that gt, lt, gte, and lte work as desired. Note that the fix changes
- # get_prep_lookup for gte and lt queries only.
+ # Custom lookups are registered to round float values correctly on gte
+ # and lt IntegerField queries.
self.assertQuerysetEqual(
Number.objects.filter(num__gt=11.9),
['<Number: 12>']