diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py index 907a59f269..e3fa8904ef 100644 --- a/django/db/backends/base/operations.py +++ b/django/db/backends/base/operations.py @@ -191,6 +191,7 @@ class BaseDatabaseOperations(object): search of the given field_name. Note that the resulting string should contain a '%s' placeholder for the value being searched against. """ + # RemovedInDjango20Warning raise NotImplementedError('Full-text search is not implemented for this database backend') def last_executed_query(self, cursor, sql, params): diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py index e71de6c4a2..8a0fe80583 100644 --- a/django/db/backends/mysql/operations.py +++ b/django/db/backends/mysql/operations.py @@ -91,6 +91,7 @@ class DatabaseOperations(BaseDatabaseOperations): return [(None, ("NULL", [], False))] def fulltext_search_sql(self, field_name): + # RemovedInDjango20Warning return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name def last_executed_query(self, cursor, sql, params): diff --git a/django/db/models/lookups.py b/django/db/models/lookups.py index 4cbf27ee56..9a7812497b 100644 --- a/django/db/models/lookups.py +++ b/django/db/models/lookups.py @@ -1,3 +1,4 @@ +import warnings from copy import copy from django.conf import settings @@ -7,6 +8,7 @@ from django.db.models.fields import ( ) from django.db.models.query_utils import RegisterLookupMixin from django.utils import timezone +from django.utils.deprecation import RemovedInDjango20Warning from django.utils.functional import cached_property from django.utils.six.moves import range @@ -373,6 +375,10 @@ class Search(BuiltinLookup): lookup_name = 'search' def as_sql(self, compiler, connection): + warnings.warn( + 'The `__search` lookup is deprecated. See the 1.10 release notes ' + 'for how to replace it.', RemovedInDjango20Warning, stacklevel=2 + ) lhs, lhs_params = self.process_lhs(compiler, connection) rhs, rhs_params = self.process_rhs(compiler, connection) sql_template = connection.ops.fulltext_search_sql(field_name=lhs) diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 81e3600bce..2da4b6d6bb 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -141,6 +141,9 @@ details on these changes. * Support for query lookups using the model name when ``Meta.default_related_name`` is set will be removed. +* The ``__search`` query lookup and the + ``DatabaseOperations.fulltext_search_sql()`` method will be removed. + .. _deprecation-removed-in-1.10: 1.10 diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt index 8a348f092e..21409f562b 100644 --- a/docs/ref/models/fields.txt +++ b/docs/ref/models/fields.txt @@ -1847,15 +1847,8 @@ Field API reference .. method:: get_prep_lookup(lookup_type, value) Prepares ``value`` to the database prior to be used in a lookup. - The ``lookup_type`` 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 ` the - ``lookup_type`` can be any ``lookup_name`` registered in the field. + 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. diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index 8781c4cdd4..6bb8cf81ce 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -2771,6 +2771,11 @@ SQL equivalent:: ``search`` ~~~~~~~~~~ +.. deprecated:: 1.10 + + See :ref:`the 1.10 release notes ` for how to + replace it. + A boolean full-text search, taking advantage of full-text indexing. This is like :lookup:`contains` but is significantly faster due to full-text indexing. diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt index 6df47d1f82..1f2ddd9457 100644 --- a/docs/releases/1.10.txt +++ b/docs/releases/1.10.txt @@ -740,6 +740,28 @@ use the default_related_name ``bars``:: >>> Foo.object.get(bars=bar) +.. _search-lookup-replacement: + +``__search`` query lookup +------------------------- + +The ``search`` lookup, which supports MySQL only and is extremely limited in +features, is deprecated. Replace it with a custom lookup:: + + from django.db import models + + class Search(models.Lookup): + lookup_name = 'search' + + def as_mysql(self, compiler, connection): + lhs, lhs_params = self.process_lhs(compiler, connection) + rhs, rhs_params = self.process_rhs(compiler, connection) + params = lhs_params + rhs_params + return 'MATCH (%s) AGAINST (%s IN BOOLEAN MODE)' % (lhs, rhs), params + + models.CharField.register_lookup(Search) + models.TextField.register_lookup(Search) + Miscellaneous ------------- diff --git a/tests/lookup/models.py b/tests/lookup/models.py index b8e47ec746..57389e90de 100644 --- a/tests/lookup/models.py +++ b/tests/lookup/models.py @@ -79,6 +79,7 @@ class Player(models.Model): # is only available when using MySQL 5.6, or when using MyISAM # tables. As 5.6 isn't common yet, lets use MyISAM table for # testing. The table is manually created by the test method. +# RemovedInDjango20Warning class MyISAMArticle(models.Model): headline = models.CharField(max_length=100) diff --git a/tests/lookup/tests.py b/tests/lookup/tests.py index aba6588ac8..d2df291388 100644 --- a/tests/lookup/tests.py +++ b/tests/lookup/tests.py @@ -7,7 +7,10 @@ from unittest import skipUnless from django.core.exceptions import FieldError from django.db import connection -from django.test import TestCase, TransactionTestCase, skipUnlessDBFeature +from django.test import ( + TestCase, TransactionTestCase, ignore_warnings, skipUnlessDBFeature, +) +from django.utils.deprecation import RemovedInDjango20Warning from .models import Article, Author, Game, MyISAMArticle, Player, Season, Tag @@ -792,6 +795,7 @@ class LookupTests(TestCase): class LookupTransactionTests(TransactionTestCase): available_apps = ['lookup'] + @ignore_warnings(category=RemovedInDjango20Warning) @skipUnless(connection.vendor == 'mysql', 'requires MySQL') def test_mysql_lookup_search(self): # To use fulltext indexes on MySQL either version 5.6 is needed, or one must use