summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsarahboyce <sarahvboyce95@gmail.com>2022-04-01 20:21:43 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-04-06 13:05:57 +0200
commit65effbdb101714ac98b3f143eaccadd8e4f08361 (patch)
tree89bbbb2d6b58c3aeae7d1149149c9382af866022
parent6991880109e35c879b71b7d9d9c154baeec12b89 (diff)
Fixed #33471 -- Made AlterField operation a noop when changing "choices".
This also allows customizing attributes of fields that don't affect a column definition.
-rw-r--r--django/db/backends/base/schema.py17
-rw-r--r--django/db/models/fields/__init__.py18
-rw-r--r--docs/howto/custom-model-fields.txt20
-rw-r--r--docs/releases/4.1.txt4
-rw-r--r--tests/schema/tests.py14
5 files changed, 58 insertions, 15 deletions
diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py
index 9d6952df21..a140a6dc61 100644
--- a/django/db/backends/base/schema.py
+++ b/django/db/backends/base/schema.py
@@ -1376,22 +1376,9 @@ class BaseDatabaseSchemaEditor:
# - changing only a field name
# - changing an attribute that doesn't affect the schema
# - adding only a db_column and the column name is not changed
- non_database_attrs = [
- "blank",
- "db_column",
- "editable",
- "error_messages",
- "help_text",
- "limit_choices_to",
- # Database-level options are not supported, see #21961.
- "on_delete",
- "related_name",
- "related_query_name",
- "validators",
- "verbose_name",
- ]
- for attr in non_database_attrs:
+ for attr in old_field.non_db_attrs:
old_kwargs.pop(attr, None)
+ for attr in new_field.non_db_attrs:
new_kwargs.pop(attr, None)
return self.quote_name(old_field.column) != self.quote_name(
new_field.column
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 72208efd04..5c5a5e0cfe 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -140,6 +140,24 @@ class Field(RegisterLookupMixin):
system_check_deprecated_details = None
system_check_removed_details = None
+ # Attributes that don't affect a column definition.
+ # These attributes are ignored when altering the field.
+ non_db_attrs = (
+ "blank",
+ "choices",
+ "db_column",
+ "editable",
+ "error_messages",
+ "help_text",
+ "limit_choices_to",
+ # Database-level options are not supported, see #21961.
+ "on_delete",
+ "related_name",
+ "related_query_name",
+ "validators",
+ "verbose_name",
+ )
+
# Field flags
hidden = False
diff --git a/docs/howto/custom-model-fields.txt b/docs/howto/custom-model-fields.txt
index 2dedf05a11..c4621c8500 100644
--- a/docs/howto/custom-model-fields.txt
+++ b/docs/howto/custom-model-fields.txt
@@ -314,6 +314,26 @@ reconstructing the field::
new_instance = MyField(*args, **kwargs)
self.assertEqual(my_field_instance.some_attribute, new_instance.some_attribute)
+.. _custom-field-non_db_attrs:
+
+Field attributes not affecting database column definition
+---------------------------------------------------------
+
+.. versionadded:: 4.1
+
+You can override ``Field.non_db_attrs`` to customize attributes of a field that
+don't affect a column definition. It's used during model migrations to detect
+no-op ``AlterField`` operations.
+
+For example::
+
+ class CommaSepField(models.Field):
+
+ @property
+ def non_db_attrs(self):
+ return super().non_db_attrs + ("separator",)
+
+
Changing a custom field's base class
------------------------------------
diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt
index 4413fdfc9a..d83da638fc 100644
--- a/docs/releases/4.1.txt
+++ b/docs/releases/4.1.txt
@@ -288,6 +288,10 @@ Models
on MariaDB and MySQL. For databases that do not support ``XOR``, the query
will be converted to an equivalent using ``AND``, ``OR``, and ``NOT``.
+* The new :ref:`Field.non_db_attrs <custom-field-non_db_attrs>` attribute
+ allows customizing attributes of fields that don't affect a column
+ definition.
+
Requests and Responses
~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/schema/tests.py b/tests/schema/tests.py
index d9e59d32dc..fcc090aaf2 100644
--- a/tests/schema/tests.py
+++ b/tests/schema/tests.py
@@ -3961,6 +3961,20 @@ class SchemaTests(TransactionTestCase):
with connection.schema_editor() as editor, self.assertNumQueries(0):
editor.alter_field(Book, new_field, old_field, strict=True)
+ def test_alter_field_choices_noop(self):
+ with connection.schema_editor() as editor:
+ editor.create_model(Author)
+ old_field = Author._meta.get_field("name")
+ new_field = CharField(
+ choices=(("Jane", "Jane"), ("Joe", "Joe")),
+ max_length=255,
+ )
+ new_field.set_attributes_from_name("name")
+ with connection.schema_editor() as editor, self.assertNumQueries(0):
+ editor.alter_field(Author, old_field, new_field, strict=True)
+ with connection.schema_editor() as editor, self.assertNumQueries(0):
+ editor.alter_field(Author, new_field, old_field, strict=True)
+
def test_add_textfield_unhashable_default(self):
# Create the table
with connection.schema_editor() as editor: