Refs #33872 -- Removed django.contrib.postgres.fields.CIText/CICharField/CIEmailField/CITextField.

Per deprecation timeline.
This commit is contained in:
Mariusz Felisiak 2023-09-13 14:03:45 +02:00
parent 6e4e5523a8
commit 04eb1b4567
12 changed files with 49 additions and 286 deletions

View File

@ -1,78 +1,45 @@
import warnings
from django.db.models import CharField, EmailField, TextField
from django.test.utils import ignore_warnings
from django.utils.deprecation import RemovedInDjango51Warning
__all__ = ["CICharField", "CIEmailField", "CIText", "CITextField"]
__all__ = ["CICharField", "CIEmailField", "CITextField"]
# RemovedInDjango51Warning.
class CIText:
def __init__(self, *args, **kwargs):
warnings.warn(
"django.contrib.postgres.fields.CIText mixin is deprecated.",
RemovedInDjango51Warning,
stacklevel=2,
)
super().__init__(*args, **kwargs)
def get_internal_type(self):
return "CI" + super().get_internal_type()
def db_type(self, connection):
return "citext"
class CICharField(CIText, CharField):
system_check_deprecated_details = {
class CICharField(CharField):
system_check_removed_details = {
"msg": (
"django.contrib.postgres.fields.CICharField is deprecated. Support for it "
"(except in historical migrations) will be removed in Django 5.1."
"django.contrib.postgres.fields.CICharField is removed except for support "
"in historical migrations."
),
"hint": (
'Use CharField(db_collation="") with a case-insensitive non-deterministic '
"collation instead."
),
"id": "fields.W905",
"id": "fields.E905",
}
def __init__(self, *args, **kwargs):
with ignore_warnings(category=RemovedInDjango51Warning):
super().__init__(*args, **kwargs)
class CIEmailField(CIText, EmailField):
system_check_deprecated_details = {
class CIEmailField(EmailField):
system_check_removed_details = {
"msg": (
"django.contrib.postgres.fields.CIEmailField is deprecated. Support for it "
"(except in historical migrations) will be removed in Django 5.1."
"django.contrib.postgres.fields.CIEmailField is removed except for support "
"in historical migrations."
),
"hint": (
'Use EmailField(db_collation="") with a case-insensitive '
"non-deterministic collation instead."
),
"id": "fields.W906",
"id": "fields.E906",
}
def __init__(self, *args, **kwargs):
with ignore_warnings(category=RemovedInDjango51Warning):
super().__init__(*args, **kwargs)
class CITextField(CIText, TextField):
system_check_deprecated_details = {
class CITextField(TextField):
system_check_removed_details = {
"msg": (
"django.contrib.postgres.fields.CITextField is deprecated. Support for it "
"(except in historical migrations) will be removed in Django 5.1."
"django.contrib.postgres.fields.CITextField is removed except for support "
"in historical migrations."
),
"hint": (
'Use TextField(db_collation="") with a case-insensitive non-deterministic '
"collation instead."
),
"id": "fields.W907",
"id": "fields.E907",
}
def __init__(self, *args, **kwargs):
with ignore_warnings(category=RemovedInDjango51Warning):
super().__init__(*args, **kwargs)

View File

@ -159,9 +159,6 @@ class DatabaseOperations(BaseDatabaseOperations):
"CharField",
"EmailField",
"TextField",
"CICharField",
"CIEmailField",
"CITextField",
):
return "%s::text"
@ -179,9 +176,6 @@ class DatabaseOperations(BaseDatabaseOperations):
):
if internal_type in ("IPAddressField", "GenericIPAddressField"):
lookup = "HOST(%s)"
# RemovedInDjango51Warning.
elif internal_type in ("CICharField", "CIEmailField", "CITextField"):
lookup = "%s::citext"
else:
lookup = "%s::text"

View File

@ -237,13 +237,19 @@ Model fields
except for support in historical migrations.
* **fields.W905**: ``django.contrib.postgres.fields.CICharField`` is
deprecated. Support for it (except in historical migrations) will be removed
in Django 5.1.
in Django 5.1. *This check appeared in Django 4.2 and 5.0*.
* **fields.E905**: ``django.contrib.postgres.fields.CICharField`` is removed
except for support in historical migrations.
* **fields.W906**: ``django.contrib.postgres.fields.CIEmailField`` is
deprecated. Support for it (except in historical migrations) will be removed
in Django 5.1.
in Django 5.1. *This check appeared in Django 4.2 and 5.0*.
* **fields.E906**: ``django.contrib.postgres.fields.CIEmailField`` is removed
except for support in historical migrations.
* **fields.W907**: ``django.contrib.postgres.fields.CITextField`` is
deprecated. Support for it (except in historical migrations) will be removed
in Django 5.1.
in Django 5.1. *This check appeared in Django 4.2 and 5.0*.
* **fields.E907**: ``django.contrib.postgres.fields.CITextField`` is removed
except for support for historical migrations.
File fields
~~~~~~~~~~~

View File

@ -277,71 +277,6 @@ transform do not change. For example:
at the database level and cannot be supported in a logical, consistent
fashion by Django.
``CIText`` fields
=================
.. class:: CIText(**options)
.. deprecated:: 4.2
A mixin to create case-insensitive text fields backed by the citext_ type.
Read about `the performance considerations`_ prior to using it.
To use ``citext``, use the :class:`.CITextExtension` operation to
:ref:`set up the citext extension <create-postgresql-extensions>` in
PostgreSQL before the first ``CreateModel`` migration operation.
If you're using an :class:`~django.contrib.postgres.fields.ArrayField`
of ``CIText`` fields, you must add ``'django.contrib.postgres'`` in your
:setting:`INSTALLED_APPS`, otherwise field values will appear as strings
like ``'{thoughts,django}'``.
Several fields that use the mixin are provided:
.. class:: CICharField(**options)
.. deprecated:: 4.2
``CICharField`` is deprecated in favor of
``CharField(db_collation="…")`` with a case-insensitive
non-deterministic collation.
.. class:: CIEmailField(**options)
.. deprecated:: 4.2
``CIEmailField`` is deprecated in favor of
``EmailField(db_collation="…")`` with a case-insensitive
non-deterministic collation.
.. class:: CITextField(**options)
.. deprecated:: 4.2
``CITextField`` is deprecated in favor of
``TextField(db_collation="…")`` with a case-insensitive
non-deterministic collation.
These fields subclass :class:`~django.db.models.CharField`,
:class:`~django.db.models.EmailField`, and
:class:`~django.db.models.TextField`, respectively.
``max_length`` won't be enforced in the database since ``citext`` behaves
similar to PostgreSQL's ``text`` type.
.. _citext: https://www.postgresql.org/docs/current/citext.html
.. _the performance considerations: https://www.postgresql.org/docs/current/citext.html#id-1.11.7.19.9
.. admonition:: Case-insensitive collations
It's preferable to use non-deterministic collations instead of the
``citext`` extension. You can create them using the
:class:`~django.contrib.postgres.operations.CreateCollation` migration
operation. For more details, see :ref:`manage-postgresql-collations` and
the PostgreSQL documentation about `non-deterministic collations`_.
.. _non-deterministic collations: https://www.postgresql.org/docs/current/collation.html#COLLATION-NONDETERMINISTIC
``HStoreField``
===============

View File

@ -260,3 +260,10 @@ to remove usage of these features.
* The ``django.contrib.auth.hashers.SHA1PasswordHasher``,
``django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher``, and
``django.contrib.auth.hashers.UnsaltedMD5PasswordHasher`` are removed.
* The model ``django.contrib.postgres.fields.CICharField``,
``django.contrib.postgres.fields.CIEmailField``, and
``django.contrib.postgres.fields.CITextField`` are removed, except for
support in historical migrations.
* The ``django.contrib.postgres.fields.CIText`` mixin is removed.

View File

@ -368,13 +368,6 @@ class Tests(TestCase):
for lookup in lookups:
with self.subTest(lookup=lookup):
self.assertIn("::text", do.lookup_cast(lookup))
# RemovedInDjango51Warning.
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_psycopg_version(self):
from django.db.backends.postgresql.base import Database, psycopg_version

View File

@ -104,46 +104,42 @@ class DeprecatedFieldsTests(SimpleTestCase):
self.assertEqual(
PostgresCIFieldsModel.check(),
[
checks.Warning(
"django.contrib.postgres.fields.CICharField is deprecated. Support "
"for it (except in historical migrations) will be removed in "
"Django 5.1.",
checks.Error(
"django.contrib.postgres.fields.CICharField is removed except for "
"support in historical migrations.",
hint=(
'Use CharField(db_collation="") with a case-insensitive '
"non-deterministic collation instead."
),
obj=PostgresCIFieldsModel._meta.get_field("ci_char"),
id="fields.W905",
id="fields.E905",
),
checks.Warning(
"django.contrib.postgres.fields.CIEmailField is deprecated. "
"Support for it (except in historical migrations) will be removed "
"in Django 5.1.",
checks.Error(
"django.contrib.postgres.fields.CIEmailField is removed except for "
"support in historical migrations.",
hint=(
'Use EmailField(db_collation="") with a case-insensitive '
"non-deterministic collation instead."
),
obj=PostgresCIFieldsModel._meta.get_field("ci_email"),
id="fields.W906",
id="fields.E906",
),
checks.Warning(
"django.contrib.postgres.fields.CITextField is deprecated. Support "
"for it (except in historical migrations) will be removed in "
"Django 5.1.",
checks.Error(
"django.contrib.postgres.fields.CITextField is removed except for "
"support in historical migrations.",
hint=(
'Use TextField(db_collation="") with a case-insensitive '
"non-deterministic collation instead."
),
obj=PostgresCIFieldsModel._meta.get_field("ci_text"),
id="fields.W907",
id="fields.E907",
),
checks.Warning(
"Base field for array has warnings:\n"
" django.contrib.postgres.fields.CITextField is deprecated. "
"Support for it (except in historical migrations) will be removed "
"in Django 5.1. (fields.W907)",
checks.Error(
"Base field for array has errors:\n"
" django.contrib.postgres.fields.CITextField is removed except "
"for support in historical migrations. (fields.E907)",
obj=PostgresCIFieldsModel._meta.get_field("array_ci_text"),
id="postgres.W004",
id="postgres.E001",
),
],
)

View File

@ -7,9 +7,6 @@ import enum
from django.db import models
try:
from django.contrib.postgres.fields import CICharField # RemovedInDjango51Warning.
from django.contrib.postgres.fields import CIEmailField # RemovedInDjango51Warning.
from django.contrib.postgres.fields import CITextField # RemovedInDjango51Warning.
from django.contrib.postgres.fields import (
ArrayField,
BigIntegerRangeField,
@ -47,9 +44,6 @@ except ImportError:
ArrayField = DummyArrayField
BigIntegerRangeField = models.Field
CICharField = models.Field # RemovedInDjango51Warning.
CIEmailField = models.Field # RemovedInDjango51Warning.
CITextField = models.Field # RemovedInDjango51Warning.
DateRangeField = models.Field
DateTimeRangeField = DummyContinuousRangeField
DecimalRangeField = DummyContinuousRangeField

View File

@ -3,9 +3,6 @@ from django.db import migrations, models
from ..fields import (
ArrayField,
BigIntegerRangeField,
CICharField,
CIEmailField,
CITextField,
DateRangeField,
DateTimeRangeField,
DecimalRangeField,
@ -290,23 +287,6 @@ class Migration(migrations.Migration):
options=None,
bases=None,
),
# RemovedInDjango51Warning.
migrations.CreateModel(
name="CITestModel",
fields=[
(
"name",
CICharField(primary_key=True, serialize=False, max_length=255),
),
("email", CIEmailField()),
("description", CITextField()),
("array_field", ArrayField(CITextField(), null=True)),
],
options={
"required_db_vendor": "postgresql",
},
bases=None,
),
migrations.CreateModel(
name="Line",
fields=[

View File

@ -3,9 +3,6 @@ from django.db import models
from .fields import (
ArrayField,
BigIntegerRangeField,
CICharField,
CIEmailField,
CITextField,
DateRangeField,
DateTimeRangeField,
DecimalRangeField,
@ -119,14 +116,6 @@ class Character(models.Model):
name = models.CharField(max_length=255)
# RemovedInDjango51Warning.
class CITestModel(PostgreSQLModel):
name = CICharField(primary_key=True, max_length=255)
email = CIEmailField()
description = CITextField()
array_field = ArrayField(CITextField(), null=True)
class Line(PostgreSQLModel):
scene = models.ForeignKey("Scene", models.CASCADE)
character = models.ForeignKey("Character", models.CASCADE)

View File

@ -1,91 +0,0 @@
# RemovedInDjango51Warning.
"""
The citext PostgreSQL extension supports indexing of case-insensitive text
strings and thus eliminates the need for operations such as iexact and other
modifiers to enforce use of an index.
"""
from django.db import IntegrityError
from django.utils.deprecation import RemovedInDjango51Warning
from . import PostgreSQLTestCase
from .models import CITestModel
class CITextTestCase(PostgreSQLTestCase):
case_sensitive_lookups = ("contains", "startswith", "endswith", "regex")
@classmethod
def setUpTestData(cls):
cls.john = CITestModel.objects.create(
name="JoHn",
email="joHn@johN.com",
description="Average Joe named JoHn",
array_field=["JoE", "jOhn"],
)
def test_equal_lowercase(self):
"""
citext removes the need for iexact as the index is case-insensitive.
"""
self.assertEqual(
CITestModel.objects.filter(name=self.john.name.lower()).count(), 1
)
self.assertEqual(
CITestModel.objects.filter(email=self.john.email.lower()).count(), 1
)
self.assertEqual(
CITestModel.objects.filter(
description=self.john.description.lower()
).count(),
1,
)
def test_fail_citext_primary_key(self):
"""
Creating an entry for a citext field used as a primary key which
clashes with an existing value isn't allowed.
"""
with self.assertRaises(IntegrityError):
CITestModel.objects.create(name="John")
def test_array_field(self):
instance = CITestModel.objects.get()
self.assertEqual(instance.array_field, self.john.array_field)
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]
)
def test_citext_deprecated(self):
from django.contrib.postgres.fields import CIText
msg = "django.contrib.postgres.fields.CIText mixin is deprecated."
with self.assertRaisesMessage(RemovedInDjango51Warning, msg):
CIText()

View File

@ -245,13 +245,6 @@ def setup_collect_tests(start_at, start_after, test_labels=None):
settings.LOGGING = log_config
settings.SILENCED_SYSTEM_CHECKS = [
"fields.W342", # ForeignKey(unique=True) -> OneToOneField
# django.contrib.postgres.fields.CICharField deprecated.
"fields.W905",
"postgres.W004",
# django.contrib.postgres.fields.CIEmailField deprecated.
"fields.W906",
# django.contrib.postgres.fields.CITextField deprecated.
"fields.W907",
]
# Load all the ALWAYS_INSTALLED_APPS.