Fixed #33471 -- Made AlterField operation a noop when changing "choices".

This also allows customizing attributes of fields that don't affect
a column definition.
This commit is contained in:
sarahboyce 2022-04-01 20:21:43 +02:00 committed by Mariusz Felisiak
parent 6991880109
commit 65effbdb10
5 changed files with 58 additions and 15 deletions

View File

@ -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

View File

@ -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

View File

@ -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
------------------------------------

View File

@ -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
~~~~~~~~~~~~~~~~~~~~~~

View File

@ -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: