Fixed #28702 -- Made query lookups for CIText fields use citext.
This commit is contained in:
parent
78c5e7b90e
commit
f0a68c2511
|
@ -4,6 +4,10 @@ __all__ = ['CICharField', 'CIEmailField', 'CIText', 'CITextField']
|
||||||
|
|
||||||
|
|
||||||
class CIText:
|
class CIText:
|
||||||
|
|
||||||
|
def get_internal_type(self):
|
||||||
|
return 'CI' + super().get_internal_type()
|
||||||
|
|
||||||
def db_type(self, connection):
|
def db_type(self, connection):
|
||||||
return 'citext'
|
return 'citext'
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,8 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||||
'istartswith', 'endswith', 'iendswith', 'regex', 'iregex'):
|
'istartswith', 'endswith', 'iendswith', 'regex', 'iregex'):
|
||||||
if internal_type in ('IPAddressField', 'GenericIPAddressField'):
|
if internal_type in ('IPAddressField', 'GenericIPAddressField'):
|
||||||
lookup = "HOST(%s)"
|
lookup = "HOST(%s)"
|
||||||
|
elif internal_type in ('CICharField', 'CIEmailField', 'CITextField'):
|
||||||
|
lookup = '%s::citext'
|
||||||
else:
|
else:
|
||||||
lookup = "%s::text"
|
lookup = "%s::text"
|
||||||
|
|
||||||
|
|
|
@ -107,11 +107,12 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
||||||
|
|
||||||
def _alter_field(self, model, old_field, new_field, old_type, new_type,
|
def _alter_field(self, model, old_field, new_field, old_type, new_type,
|
||||||
old_db_params, new_db_params, strict=False):
|
old_db_params, new_db_params, strict=False):
|
||||||
# Drop indexes on varchar/text columns that are changing to a different
|
# Drop indexes on varchar/text/citext columns that are changing to a
|
||||||
# type.
|
# different type.
|
||||||
if (old_field.db_index or old_field.unique) and (
|
if (old_field.db_index or old_field.unique) and (
|
||||||
(old_type.startswith('varchar') and not new_type.startswith('varchar')) or
|
(old_type.startswith('varchar') and not new_type.startswith('varchar')) or
|
||||||
(old_type.startswith('text') and not new_type.startswith('text'))
|
(old_type.startswith('text') and not new_type.startswith('text')) or
|
||||||
|
(old_type.startswith('citext') and not new_type.startswith('citext'))
|
||||||
):
|
):
|
||||||
index_name = self._create_index_name(model._meta.db_table, [old_field.column], suffix='_like')
|
index_name = self._create_index_name(model._meta.db_table, [old_field.column], suffix='_like')
|
||||||
self.execute(self._delete_constraint_sql(self.sql_delete_index, model, index_name))
|
self.execute(self._delete_constraint_sql(self.sql_delete_index, model, index_name))
|
||||||
|
|
|
@ -24,3 +24,6 @@ Bugfixes
|
||||||
|
|
||||||
* Fixed crash on SQLite and MySQL when ordering by a filtered subquery that
|
* Fixed crash on SQLite and MySQL when ordering by a filtered subquery that
|
||||||
uses ``nulls_first`` or ``nulls_last`` (:ticket:`28848`).
|
uses ``nulls_first`` or ``nulls_last`` (:ticket:`28848`).
|
||||||
|
|
||||||
|
* Made query lookups for ``CICharField``, ``CIEmailField``, and ``CITextField``
|
||||||
|
use a ``citext`` cast (:ticket:`28702`).
|
||||||
|
|
|
@ -138,6 +138,10 @@ class Tests(TestCase):
|
||||||
for lookup in lookups:
|
for lookup in lookups:
|
||||||
with self.subTest(lookup=lookup):
|
with self.subTest(lookup=lookup):
|
||||||
self.assertIn('::text', do.lookup_cast(lookup))
|
self.assertIn('::text', do.lookup_cast(lookup))
|
||||||
|
for lookup in lookups:
|
||||||
|
for field_type in ('CICharField', 'CIEmailField', 'CITextField'):
|
||||||
|
with self.subTest(lookup=lookup, field_type=field_type):
|
||||||
|
self.assertIn('::citext', do.lookup_cast(lookup, internal_type=field_type))
|
||||||
|
|
||||||
def test_correct_extraction_psycopg2_version(self):
|
def test_correct_extraction_psycopg2_version(self):
|
||||||
from django.db.backends.postgresql.base import psycopg2_version
|
from django.db.backends.postgresql.base import psycopg2_version
|
||||||
|
|
|
@ -12,6 +12,7 @@ from .models import CITestModel
|
||||||
|
|
||||||
@modify_settings(INSTALLED_APPS={'append': 'django.contrib.postgres'})
|
@modify_settings(INSTALLED_APPS={'append': 'django.contrib.postgres'})
|
||||||
class CITextTestCase(PostgreSQLTestCase):
|
class CITextTestCase(PostgreSQLTestCase):
|
||||||
|
case_sensitive_lookups = ('contains', 'startswith', 'endswith', 'regex')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
@ -42,3 +43,21 @@ class CITextTestCase(PostgreSQLTestCase):
|
||||||
instance = CITestModel.objects.get()
|
instance = CITestModel.objects.get()
|
||||||
self.assertEqual(instance.array_field, self.john.array_field)
|
self.assertEqual(instance.array_field, self.john.array_field)
|
||||||
self.assertTrue(CITestModel.objects.filter(array_field__contains=['joe']).exists())
|
self.assertTrue(CITestModel.objects.filter(array_field__contains=['joe']).exists())
|
||||||
|
|
||||||
|
def test_lookups_name_char(self):
|
||||||
|
for lookup in self.case_sensitive_lookups:
|
||||||
|
with self.subTest(lookup=lookup):
|
||||||
|
query = {'name__{}'.format(lookup): 'john'}
|
||||||
|
self.assertSequenceEqual(CITestModel.objects.filter(**query), [self.john])
|
||||||
|
|
||||||
|
def test_lookups_description_text(self):
|
||||||
|
for lookup, string in zip(self.case_sensitive_lookups, ('average', 'average joe', 'john', 'Joe.named')):
|
||||||
|
with self.subTest(lookup=lookup, string=string):
|
||||||
|
query = {'description__{}'.format(lookup): string}
|
||||||
|
self.assertSequenceEqual(CITestModel.objects.filter(**query), [self.john])
|
||||||
|
|
||||||
|
def test_lookups_email(self):
|
||||||
|
for lookup, string in zip(self.case_sensitive_lookups, ('john', 'john', 'john.com', 'john.com')):
|
||||||
|
with self.subTest(lookup=lookup, string=string):
|
||||||
|
query = {'email__{}'.format(lookup): string}
|
||||||
|
self.assertSequenceEqual(CITestModel.objects.filter(**query), [self.john])
|
||||||
|
|
Loading…
Reference in New Issue