diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py index f46ac955ba..0539d71539 100644 --- a/django/db/backends/base/operations.py +++ b/django/db/backends/base/operations.py @@ -191,15 +191,6 @@ class BaseDatabaseOperations(object): else: return 'FOR UPDATE' - def fulltext_search_sql(self, field_name): - """ - Returns the SQL WHERE clause to use in order to perform a full-text - 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): """ Returns a string of the query last executed by the given cursor, with diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py index 43c39441c2..6a55d7a88f 100644 --- a/django/db/backends/mysql/operations.py +++ b/django/db/backends/mysql/operations.py @@ -110,10 +110,6 @@ 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): # With MySQLdb, cursor objects have an (undocumented) "_last_executed" # attribute where the exact query sent to the database is saved. diff --git a/django/db/models/lookups.py b/django/db/models/lookups.py index 8f7db0723c..b8093ddc34 100644 --- a/django/db/models/lookups.py +++ b/django/db/models/lookups.py @@ -1,6 +1,5 @@ import itertools import math -import warnings from copy import copy from decimal import Decimal @@ -10,7 +9,6 @@ from django.db.models.fields import ( DateTimeField, DecimalField, Field, IntegerField, ) from django.db.models.query_utils import RegisterLookupMixin -from django.utils.deprecation import RemovedInDjango20Warning from django.utils.functional import cached_property from django.utils.six.moves import range @@ -509,22 +507,6 @@ class IsNull(BuiltinLookup): return "%s IS NOT NULL" % sql, params -@Field.register_lookup -class Search(BuiltinLookup): - lookup_name = 'search' - prepare_rhs = False - - 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) - return sql_template, lhs_params + rhs_params - - @Field.register_lookup class Regex(BuiltinLookup): lookup_name = 'regex' diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index ee48a3c6f0..ec71fe0294 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -2918,33 +2918,6 @@ SQL equivalent:: SELECT ... WHERE pub_date IS NULL; -.. fieldlookup:: search - -``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. - -Example:: - - Entry.objects.filter(headline__search="+Django -jazz Python") - -SQL equivalent:: - - SELECT ... WHERE MATCH(tablename, headline) AGAINST (+Django -jazz Python IN BOOLEAN MODE); - -Note this is only available in MySQL and requires direct manipulation of the -database to add the full-text index. By default Django uses BOOLEAN MODE for -full text searches. See the `MySQL documentation`_ for additional details. - -.. _MySQL documentation: https://dev.mysql.com/doc/refman/en/fulltext-boolean.html - .. fieldlookup:: regex ``regex`` diff --git a/docs/releases/2.0.txt b/docs/releases/2.0.txt index 5734fdd5b2..f0f5ff6566 100644 --- a/docs/releases/2.0.txt +++ b/docs/releases/2.0.txt @@ -353,3 +353,5 @@ these features. * Support for query lookups using the model name when ``Meta.default_related_name`` is set is removed. + +* The MySQL ``__search`` lookup is removed. diff --git a/tests/lookup/models.py b/tests/lookup/models.py index e8479f11d1..cfc0cecb99 100644 --- a/tests/lookup/models.py +++ b/tests/lookup/models.py @@ -75,19 +75,6 @@ class Player(models.Model): return self.name -# To test __search lookup a fulltext index is needed. This -# 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) - - class Meta: - db_table = 'myisam_article' - managed = False - - class Product(models.Model): name = models.CharField(max_length=80) qty_target = models.DecimalField(max_digits=6, decimal_places=2) diff --git a/tests/lookup/tests.py b/tests/lookup/tests.py index 1c8dd986fc..78e68dfc4a 100644 --- a/tests/lookup/tests.py +++ b/tests/lookup/tests.py @@ -3,16 +3,11 @@ from __future__ import unicode_literals import collections from datetime import datetime from operator import attrgetter -from unittest import skipUnless from django.core.exceptions import FieldError -from django.db import connection -from django.test import ( - TestCase, TransactionTestCase, ignore_warnings, skipUnlessDBFeature, -) -from django.utils.deprecation import RemovedInDjango20Warning +from django.test import TestCase, skipUnlessDBFeature -from .models import Article, Author, Game, MyISAMArticle, Player, Season, Tag +from .models import Article, Author, Game, Player, Season, Tag class LookupTests(TestCase): @@ -775,26 +770,3 @@ class LookupTests(TestCase): ''], ordered=False ) - - -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 - # MyISAM tables. Neither of these combinations is currently available on CI, so - # lets manually create a MyISAM table for Article model. - with connection.cursor() as cursor: - cursor.execute( - "CREATE TEMPORARY TABLE myisam_article (" - " id INTEGER PRIMARY KEY AUTO_INCREMENT, " - " headline VARCHAR(100) NOT NULL " - ") ENGINE MYISAM") - dr = MyISAMArticle.objects.create(headline='Django Reinhardt') - MyISAMArticle.objects.create(headline='Ringo Star') - # NOTE: Needs to be created after the article has been saved. - cursor.execute( - 'CREATE FULLTEXT INDEX myisam_article_ft ON myisam_article (headline)') - self.assertSequenceEqual(MyISAMArticle.objects.filter(headline__search='Reinhardt'), [dr])