[Django] #28305: AlterField migration tries to alter column that still has a foreign key contraint

26 views
Skip to first unread message

Django

unread,
Jun 13, 2017, 4:49:38 PM6/13/17
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas | Owner: nobody
Backx |
Type: | Status: new
Uncategorized |
Component: | Version: master
Migrations | Keywords: mysql,
Severity: Normal | onetoonefield, utf8mb4
Triage Stage: | Has patch: 0
Unreviewed |
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------+-------------------------------------
''A test case has been created here: https://github.com/AndreasBackx
/Django-OneToOneFieldBug-Example. How to run the test case is described
there.''

There is a migration issue when moving from Django 1.10.7 to Django
1.11.2. This sample I made is a slimmed down version from an internal
library which moved from utf8 to utf8mb4 in MySQL. This required the keys
to be changed from 255 characters to 191 characters in order to be
compatible with older versions of MySQL but the reason is irrelevant here.
The issue is that Django 1.11.2+ tries to change the 'id' column of which
the max_length has been changed from 255 to 191 that still has a
constraint. It results in the following error:

{{{
django.db.utils.OperationalError: (1833, "Cannot change column 'id': used
in a foreign key constraint
'myapp_agreement_member_id_0dc75c75_fk_myapp_member_id' of table
'test_onetoone.myapp_agreement'")
}}}

This is the full traceback:

{{{
Applying myapp.0002_utf8mb4...Traceback (most recent call last):
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/db/backends/utils.py", line 65, in execute
return self.cursor.execute(sql, params)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/db/backends/mysql/base.py", line 101, in execute
return self.cursor.execute(query, args)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-packages/MySQLdb/cursors.py",
line 250, in execute
self.errorhandler(self, exc, value)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/MySQLdb/connections.py", line 50, in defaulterrorhandler
raise errorvalue
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-packages/MySQLdb/cursors.py",
line 247, in execute
res = self._query(query)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-packages/MySQLdb/cursors.py",
line 411, in _query
rowcount = self._do_query(q)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-packages/MySQLdb/cursors.py",
line 374, in _do_query
db.query(q)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/MySQLdb/connections.py", line 292, in query
_mysql.connection.query(self, query)
_mysql_exceptions.OperationalError: (1833, "Cannot change column 'id':
used in a foreign key constraint
'myapp_agreement_member_id_0dc75c75_fk_myapp_member_id' of table
'test_onetoone.myapp_agreement'")

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "manage.py", line 22, in <module>
execute_from_command_line(sys.argv)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/core/management/__init__.py", line 363, in
execute_from_command_line
utility.execute()
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/core/management/__init__.py", line 355, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/core/management/commands/test.py", line 29, in
run_from_argv
super(Command, self).run_from_argv(argv)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/core/management/base.py", line 283, in run_from_argv
self.execute(*args, **cmd_options)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/core/management/base.py", line 330, in execute
output = self.handle(*args, **options)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/core/management/commands/test.py", line 62, in handle
failures = test_runner.run_tests(test_labels)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/test/runner.py", line 601, in run_tests
old_config = self.setup_databases()
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/test/runner.py", line 546, in setup_databases
self.parallel, **kwargs
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/test/utils.py", line 187, in setup_databases
serialize=connection.settings_dict.get('TEST', {}).get('SERIALIZE',
True),
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/db/backends/base/creation.py", line 69, in create_test_db
run_syncdb=True,
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/core/management/__init__.py", line 130, in call_command
return command.execute(*args, **defaults)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/core/management/base.py", line 330, in execute
output = self.handle(*args, **options)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/core/management/commands/migrate.py", line 204, in handle
fake_initial=fake_initial,
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/db/migrations/executor.py", line 115, in migrate
state = self._migrate_all_forwards(state, plan, full_plan, fake=fake,
fake_initial=fake_initial)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/db/migrations/executor.py", line 145, in
_migrate_all_forwards
state = self.apply_migration(state, migration, fake=fake,
fake_initial=fake_initial)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/db/migrations/executor.py", line 244, in apply_migration
state = migration.apply(state, schema_editor)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/db/migrations/migration.py", line 129, in apply
operation.database_forwards(self.app_label, schema_editor, old_state,
project_state)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/db/migrations/operations/fields.py", line 215, in
database_forwards
schema_editor.alter_field(from_model, from_field, to_field)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/db/backends/base/schema.py", line 515, in alter_field
old_db_params, new_db_params, strict)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/db/backends/base/schema.py", line 684, in _alter_field
params,
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/db/backends/base/schema.py", line 120, in execute
cursor.execute(sql, params)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/db/backends/utils.py", line 65, in execute
return self.cursor.execute(sql, params)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-packages/django/db/utils.py",
line 94, in __exit__
six.reraise(dj_exc_type, dj_exc_value, traceback)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/utils/six.py", line 685, in reraise
raise value.with_traceback(tb)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/db/backends/utils.py", line 65, in execute
return self.cursor.execute(sql, params)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/django/db/backends/mysql/base.py", line 101, in execute
return self.cursor.execute(query, args)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-packages/MySQLdb/cursors.py",
line 250, in execute
self.errorhandler(self, exc, value)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/MySQLdb/connections.py", line 50, in defaulterrorhandler
raise errorvalue
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-packages/MySQLdb/cursors.py",
line 247, in execute
res = self._query(query)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-packages/MySQLdb/cursors.py",
line 411, in _query
rowcount = self._do_query(q)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-packages/MySQLdb/cursors.py",
line 374, in _do_query
db.query(q)
File "/path/to/project/django-onetoone-
test/.tox/py35-django-111/lib/python3.5/site-
packages/MySQLdb/connections.py", line 292, in query
_mysql.connection.query(self, query)
django.db.utils.OperationalError: (1833, "Cannot change column 'id': used
in a foreign key constraint
'myapp_agreement_member_id_0dc75c75_fk_myapp_member_id' of table
'test_onetoone.myapp_agreement'")

}}}

Here is some extra information that I thought might be relevant.

This is the output of ''onetoone/manage.py sqlmigrate myapp 0002'' on
Django 1.11.2 (after resetting the database and running migrate which
failed on the second migration):
{{{
BEGIN;
--
-- Create model Group
--
CREATE TABLE `myapp_group` (`id` varchar(255) NOT NULL PRIMARY KEY);
--
-- Create model Member
--
CREATE TABLE `myapp_member` (`id` varchar(255) NOT NULL PRIMARY KEY,
`group_id` varchar(255) NULL);
--
-- Create model Agreement
--
CREATE TABLE `myapp_agreement` (`id` varchar(255) NOT NULL PRIMARY KEY,
`member_id` varchar(255) NULL UNIQUE);
ALTER TABLE `myapp_member` ADD CONSTRAINT
`myapp_member_group_id_31ff18be_fk_myapp_group_id` FOREIGN KEY
(`group_id`) REFERENCES `myapp_group` (`id`);
ALTER TABLE `myapp_agreement` ADD CONSTRAINT
`myapp_agreement_member_id_0dc75c75_fk_myapp_member_id` FOREIGN KEY
(`member_id`) REFERENCES `myapp_member` (`id`);
COMMIT;
BEGIN;
--
-- Alter field id on group
--
ALTER TABLE `myapp_member` DROP FOREIGN KEY
`myapp_member_group_id_31ff18be_fk`;
ALTER TABLE `myapp_group` MODIFY `id` varchar(191) NOT NULL;
ALTER TABLE `myapp_member` MODIFY `group_id` varchar(191) NULL;
ALTER TABLE `myapp_member` ADD CONSTRAINT
`myapp_member_group_id_31ff18be_fk` FOREIGN KEY (`group_id`) REFERENCES
`myapp_group` (`id`);
--
-- Alter field id on member
--
ALTER TABLE `myapp_agreement` DROP FOREIGN KEY
`myapp_agreement_member_id_0dc75c75_fk_myapp_member_id`;
ALTER TABLE `myapp_member` MODIFY `id` varchar(191) NOT NULL;
ALTER TABLE `myapp_agreement` MODIFY `member_id` varchar(191) NULL;
ALTER TABLE `myapp_agreement` ADD CONSTRAINT
`myapp_agreement_member_id_0dc75c75_fk` FOREIGN KEY (`member_id`)
REFERENCES `myapp_member` (`id`);
--
-- Alter field id on agreement
--
ALTER TABLE `myapp_agreement` MODIFY `id` varchar(191) NOT NULL;
COMMIT;

}}}


The same but for Django 1.10.7 (the second migration succeeded here):

{{{
BEGIN;
--
-- Create model Group
--
CREATE TABLE `myapp_group` (`id` varchar(255) NOT NULL PRIMARY KEY);
--
-- Create model Member
--
CREATE TABLE `myapp_member` (`id` varchar(255) NOT NULL PRIMARY KEY,
`group_id` varchar(255) NULL);
--
-- Create model Agreement
--
CREATE TABLE `myapp_agreement` (`id` varchar(255) NOT NULL PRIMARY KEY,
`member_id` varchar(255) NULL UNIQUE);
ALTER TABLE `myapp_member` ADD CONSTRAINT
`myapp_member_group_id_31ff18be_fk_myapp_group_id` FOREIGN KEY
(`group_id`) REFERENCES `myapp_group` (`id`);
ALTER TABLE `myapp_agreement` ADD CONSTRAINT
`myapp_agreement_member_id_0dc75c75_fk_myapp_member_id` FOREIGN KEY
(`member_id`) REFERENCES `myapp_member` (`id`);
COMMIT;
BEGIN;
--
-- Alter field id on group
--
ALTER TABLE `myapp_member` DROP FOREIGN KEY
`myapp_member_group_id_31ff18be_fk`;
ALTER TABLE `myapp_group` MODIFY `id` varchar(191) NOT NULL;
ALTER TABLE `myapp_member` MODIFY `group_id` varchar(191) NULL;
ALTER TABLE `myapp_member` ADD CONSTRAINT
`myapp_member_group_id_31ff18be_fk` FOREIGN KEY (`group_id`) REFERENCES
`myapp_group` (`id`);
--
-- Alter field id on member
--
ALTER TABLE `myapp_agreement` DROP FOREIGN KEY
`myapp_agreement_member_id_0dc75c75_fk`;
ALTER TABLE `myapp_member` MODIFY `id` varchar(191) NOT NULL;
ALTER TABLE `myapp_agreement` MODIFY `member_id` varchar(191) NULL;
ALTER TABLE `myapp_agreement` ADD CONSTRAINT
`myapp_agreement_member_id_0dc75c75_fk` FOREIGN KEY (`member_id`)
REFERENCES `myapp_member` (`id`);
--
-- Alter field id on agreement
--
ALTER TABLE `myapp_agreement` MODIFY `id` varchar(191) NOT NULL;
COMMIT;
}}}

This is the diff for these files:

{{{
--- django10 2017-06-13 22:18:21.099594038 +0200
+++ django11 2017-06-13 22:17:38.821556738 +0200
@@ -25,7 +25,7 @@
--
-- Alter field id on member
--
-ALTER TABLE `myapp_agreement` DROP FOREIGN KEY
`myapp_agreement_member_id_0dc75c75_fk`;
+ALTER TABLE `myapp_agreement` DROP FOREIGN KEY
`myapp_agreement_member_id_0dc75c75_fk_myapp_member_id`;
ALTER TABLE `myapp_member` MODIFY `id` varchar(191) NOT NULL;
ALTER TABLE `myapp_agreement` MODIFY `member_id` varchar(191) NULL;
ALTER TABLE `myapp_agreement` ADD CONSTRAINT
`myapp_agreement_member_id_0dc75c75_fk` FOREIGN KEY (`member_id`)
REFERENCES `myapp_member` (`id`);
}}}

I'm not too familiar with the database part of the Django core and
wouldn't know where to begin. I thought to add as much information as I
could, I can provide more if need be, but the test project should allow
you to make your own assessments.

--
Ticket URL: <https://code.djangoproject.com/ticket/28305>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Jun 13, 2017, 4:54:35 PM6/13/17
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Uncategorized | Status: new
Component: Migrations | Version: master
Severity: Normal | Resolution:
Keywords: mysql, | Triage Stage:
onetoonefield, utf8mb4, foreign | Unreviewed
key |
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Andreas Backx):

* keywords: mysql, onetoonefield, utf8mb4 => mysql, onetoonefield,
utf8mb4, foreign key


--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:1>

Django

unread,
Jun 13, 2017, 4:58:33 PM6/13/17
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Uncategorized | Status: new
Component: Migrations | Version: master
Severity: Normal | Resolution:
Keywords: mysql, | Triage Stage:
onetoonefield, utf8mb4, foreign | Unreviewed
key |
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by Andreas Backx:

Old description:

New description:

''A test case has been created here: https://github.com/AndreasBackx
/Django-OneToOneFieldBug-Example. How to run the test case is described

there. The bug applies to Django 1.11.2 and the master branch of Django.
There are tests for 1.10.7 which succeed and tests that fail for 1.11.2
and the master branch.''

}}}

}}}

--

--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:2>

Django

unread,
Jun 13, 2017, 4:59:23 PM6/13/17
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Bug | Status: new

Component: Migrations | Version: master
Severity: Normal | Resolution:
Keywords: mysql, | Triage Stage:
onetoonefield, utf8mb4, foreign | Unreviewed
key |
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Andreas Backx):

* type: Uncategorized => Bug


--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:3>

Django

unread,
Jun 13, 2017, 9:06:11 PM6/13/17
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Bug | Status: new
Component: Migrations | Version: master
Severity: Normal | Resolution:
Keywords: mysql, | Triage Stage:
onetoonefield, utf8mb4, foreign | Unreviewed
key |
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Tim Graham):

A starting point would be to
[https://docs.djangoproject.com/en/dev/internals/contributing/triaging-
tickets/#bisecting-a-regression bisect] to find the commit where the
behavior changed. If writing a test for Django's test suite is too
difficult for you, you can always bisect manually using your test project.

--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:4>

Django

unread,
Jun 14, 2017, 7:02:14 AM6/14/17
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Bug | Status: new
Component: Migrations | Version: master
Severity: Normal | Resolution:
Keywords: mysql, | Triage Stage:
onetoonefield, utf8mb4, foreign | Unreviewed
key |
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Andreas Backx):

I'll try to create a test for the Django's test suite hopefully this week
and bisect it that way as something came up that needs to prioritisation
here. Should I add the failing test in the first commit and add a patch in
the second commit of the PR?

--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:5>

Django

unread,
Jun 14, 2017, 7:03:19 AM6/14/17
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Bug | Status: new
Component: Migrations | Version: master
Severity: Normal | Resolution:
Keywords: mysql, | Triage Stage:
onetoonefield, utf8mb4, foreign | Unreviewed
key |
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Tim Graham):

Test and fix can be in the same commit, see the PatchReviewChecklist.

--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:6>

Django

unread,
Jun 14, 2017, 7:04:12 AM6/14/17
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Bug | Status: new
Component: Migrations | Version: master
Severity: Normal | Resolution:
Keywords: mysql, | Triage Stage:
onetoonefield, utf8mb4, foreign | Unreviewed
key |
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Andreas Backx):

Alright, thank you for the link. Will do.

--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:7>

Django

unread,
Jun 15, 2017, 1:51:03 PM6/15/17
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Bug | Status: new
Component: Migrations | Version: master
Severity: Normal | Resolution:
Keywords: mysql, | Triage Stage:
onetoonefield, utf8mb4, foreign | Unreviewed
key |
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Tim Graham):

* cc: Markus Holtermann (added)


Comment:

Bisected to 45ded053b1f4320284aa5dac63052f6d1baefea9, Fixed #27666 --
Delayed rendering of recursivly related models in migration operations.

--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:8>

Django

unread,
Jun 17, 2017, 6:29:43 PM6/17/17
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Bug | Status: new
Component: Migrations | Version: master
Severity: Release blocker | Resolution:
Keywords: mysql, | Triage Stage: Accepted
onetoonefield, utf8mb4, foreign |
key |
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Tim Graham):

* severity: Normal => Release blocker
* stage: Unreviewed => Accepted


--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:9>

Django

unread,
Jun 19, 2017, 6:12:03 AM6/19/17
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Bug | Status: new
Component: Migrations | Version: master
Severity: Release blocker | Resolution:
Keywords: mysql, | Triage Stage: Accepted
onetoonefield, utf8mb4, foreign |
key |
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Markus Holtermann):

Thanks for the report Andreas. I took a shot at this. Here's what I found:

* This issue doesn't seem to manifest on PostgreSQL
* The SQL created in `sqlmigrate` (which you posted above) works (you can
run that manually and the constraints get dropped and recreated as
intended)
* `manage.py migrate` for whatever reason does _not_ run the `ALTER TABLE
myapp_agreement DROP FOREIGN KEY
myapp_agreement_member_id_0dc75c75_fk_myapp_member_id;` on 1.11.x which is
due to `_related_non_m2m_objects()` in
https://github.com/django/django/blob/stable/1.11.x/django/db/backends/base/schema.py#L551
not returning related objects.

{{{
$ python manage.py sqlmigrate myapp 0002 --settings
onetoone.mysql_settings
>
/home/markus/Coding/django/django/db/backends/base/schema.py(549)_alter_field()
548 import ipdb; ipdb.set_trace()
--> 549 if old_field.primary_key and new_field.primary_key and
old_type != new_type:
550 # '_meta.related_field' also contains M2M reverse
fields, these

ipdb> ipdb> tuple(_related_non_m2m_objects(old_field, new_field))
((<ManyToOneRel: myapp.member>, <ManyToOneRel: myapp.member>),)
ipdb> c


ALTER TABLE `myapp_member` DROP FOREIGN KEY

`myapp_member_group_id_31ff18be_fk`; (params ())
ALTER TABLE `myapp_group` MODIFY `id` varchar(191) NOT NULL; (params [])
ALTER TABLE `myapp_member` MODIFY `group_id` varchar(191) NULL; (params
[])


ALTER TABLE `myapp_member` ADD CONSTRAINT
`myapp_member_group_id_31ff18be_fk` FOREIGN KEY (`group_id`) REFERENCES

`myapp_group` (`id`); (params ())
>
/home/markus/Coding/django/django/db/backends/base/schema.py(549)_alter_field()
548 import ipdb; ipdb.set_trace()
--> 549 if old_field.primary_key and new_field.primary_key and
old_type != new_type:
550 # '_meta.related_field' also contains M2M reverse
fields, these

ipdb> tuple(_related_non_m2m_objects(old_field, new_field))
((<OneToOneRel: myapp.agreement>, <OneToOneRel: myapp.agreement>),)
ipdb> c


ALTER TABLE `myapp_agreement` DROP FOREIGN KEY

`myapp_agreement_member_id_0dc75c75_fk_myapp_member_id`; (params ())
ALTER TABLE `myapp_member` MODIFY `id` varchar(191) NOT NULL; (params [])


ALTER TABLE `myapp_agreement` MODIFY `member_id` varchar(191) NULL;

(params [])


ALTER TABLE `myapp_agreement` ADD CONSTRAINT

`myapp_agreement_member_id_0dc75c75_fk` FOREIGN KEY (`member_id`)

REFERENCES `myapp_member` (`id`); (params ())
>
/home/markus/Coding/django/django/db/backends/base/schema.py(549)_alter_field()
548 import ipdb; ipdb.set_trace()
--> 549 if old_field.primary_key and new_field.primary_key and
old_type != new_type:
550 # '_meta.related_field' also contains M2M reverse
fields, these

ipdb> c
ALTER TABLE `myapp_agreement` MODIFY `id` varchar(191) NOT NULL; (params
[])

{{{
$ python manage.py migrate myapp 0002 --settings onetoone.mysql_settings
System check identified some issues:

WARNINGS:
?: (mysql.W002) MySQL Strict Mode is not set for database connection
'default'
HINT: MySQL's Strict Mode fixes many data integrity problems in
MySQL, such as data truncation upon insertion, by escalating warnings into
errors. It is strongly recommended you activate it. See:
https://docs.djangoproject.com/en/dev/ref/databases/#mysql-sql-mode
Operations to perform:
Target specific migration: 0002_utf8mb4, from myapp
Running migrations:
Applying myapp.0002_utf8mb4...>
/home/markus/Coding/django/django/db/backends/base/schema.py(549)_alter_field()
548 import ipdb; ipdb.set_trace()
--> 549 if old_field.primary_key and new_field.primary_key and
old_type != new_type:
550 # '_meta.related_field' also contains M2M reverse
fields, these

ipdb> tuple(_related_non_m2m_objects(old_field, new_field))
((<ManyToOneRel: myapp.member>, <ManyToOneRel: myapp.member>),)
ipdb> c


ALTER TABLE `myapp_member` DROP FOREIGN KEY

`myapp_member_group_id_31ff18be_fk`; (params ())
ALTER TABLE `myapp_group` MODIFY `id` varchar(191) NOT NULL; (params [])
ALTER TABLE `myapp_member` MODIFY `group_id` varchar(191) NULL; (params
[])


ALTER TABLE `myapp_member` ADD CONSTRAINT
`myapp_member_group_id_31ff18be_fk` FOREIGN KEY (`group_id`) REFERENCES

`myapp_group` (`id`); (params ())
>
/home/markus/Coding/django/django/db/backends/base/schema.py(549)_alter_field()
548 import ipdb; ipdb.set_trace()
--> 549 if old_field.primary_key and new_field.primary_key and
old_type != new_type:
550 # '_meta.related_field' also contains M2M reverse
fields, these

ipdb> tuple(_related_non_m2m_objects(old_field, new_field))
()
ipdb> c
ALTER TABLE `myapp_member` MODIFY `id` varchar(191) NOT NULL; (params [])


Traceback (most recent call last):

File "/home/markus/Coding/django/django/db/backends/utils.py", line 65,


in execute
return self.cursor.execute(sql, params)

File "/home/markus/Coding/django/django/db/backends/mysql/base.py", line


101, in execute
return self.cursor.execute(query, args)

File "/home/markus/.venvs/django-py35/lib/python3.5/site-
packages/MySQLdb/cursors.py", line 226, in execute
self.errorhandler(self, exc, value)
File "/home/markus/.venvs/django-py35/lib/python3.5/site-
packages/MySQLdb/connections.py", line 36, in defaulterrorhandler
raise errorvalue
File "/home/markus/.venvs/django-py35/lib/python3.5/site-
packages/MySQLdb/cursors.py", line 217, in execute
res = self._query(query)
File "/home/markus/.venvs/django-py35/lib/python3.5/site-
packages/MySQLdb/cursors.py", line 378, in _query
rowcount = self._do_query(q)
File "/home/markus/.venvs/django-py35/lib/python3.5/site-
packages/MySQLdb/cursors.py", line 341, in _do_query
db.query(q)
File "/home/markus/.venvs/django-py35/lib/python3.5/site-
packages/MySQLdb/connections.py", line 280, in query


_mysql.connection.query(self, query)
_mysql_exceptions.OperationalError: (1833, "Cannot change column 'id':
used in a foreign key constraint
'myapp_agreement_member_id_0dc75c75_fk_myapp_member_id' of table

'django.myapp_agreement'")
}}}

Both commands are run on `1.11.3.dev20170617223301`

--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:10>

Django

unread,
Jun 28, 2017, 11:29:16 AM6/28/17
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Bug | Status: new
Component: Migrations | Version: master
Severity: Release blocker | Resolution:
Keywords: mysql, | Triage Stage: Accepted
onetoonefield, utf8mb4, foreign |
key |
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Tim Graham):

Markus, do you have any suggestions about how to fix this? I confirmed
that reverting the
[https://github.com/django/django/commit/45ded053b1f4320284aa5dac63052f6d1baefea9
#diff-efb7d00c2383393046c9c5d842f45499R201 delayed rendering changes] to
`AlterField` fixes it. Maybe the reloading shouldn't be delayed if a field
is pointed to by a foreign key? (i.e. reloading should be delayed for both
forward and reverse relations)

--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:11>

Django

unread,
Jun 30, 2017, 4:48:37 PM6/30/17
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Bug | Status: new
Component: Migrations | Version: master
Severity: Release blocker | Resolution:
Keywords: mysql, | Triage Stage: Accepted
onetoonefield, utf8mb4, foreign |
key |
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 1

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Tim Graham):

* needs_better_patch: 0 => 1
* has_patch: 0 => 1


Comment:

[https://github.com/django/django/pull/8692 WIP PR]

--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:12>

Django

unread,
Dec 1, 2017, 7:48:28 PM12/1/17
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Bug | Status: closed
Component: Migrations | Version: master
Severity: Release blocker | Resolution: fixed

Keywords: mysql, | Triage Stage: Accepted
onetoonefield, utf8mb4, foreign |
key |
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 1

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Tim Graham <timograham@…>):

* status: new => closed
* resolution: => fixed


Comment:

In [changeset:"c3e0adcad8d8ba94b33cabd137056166ed36dae0" c3e0adc]:
{{{
#!CommitTicketReference repository=""
revision="c3e0adcad8d8ba94b33cabd137056166ed36dae0"
Fixed #28305 -- Fixed "Cannot change column 'x': used in a foreign key
constraint" crash on MySQL with a sequence of AlterField or RenameField
operations.

Regression in 45ded053b1f4320284aa5dac63052f6d1baefea9.
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:13>

Django

unread,
Dec 1, 2017, 7:50:49 PM12/1/17
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Bug | Status: closed
Component: Migrations | Version: master
Severity: Release blocker | Resolution: fixed
Keywords: mysql, | Triage Stage: Accepted
onetoonefield, utf8mb4, foreign |
key |
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 1

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Tim Graham <timograham@…>):

In [changeset:"4dc35e126d8461589b4b4bc47b6f6bd6cc4a1455" 4dc35e12]:
{{{
#!CommitTicketReference repository=""
revision="4dc35e126d8461589b4b4bc47b6f6bd6cc4a1455"
[2.0.x] Fixed #28305 -- Fixed "Cannot change column 'x': used in a foreign
key constraint" crash on MySQL with a sequence of AlterField or
RenameField operations.

Regression in 45ded053b1f4320284aa5dac63052f6d1baefea9.

Backport of c3e0adcad8d8ba94b33cabd137056166ed36dae0 from master
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:14>

Django

unread,
Dec 1, 2017, 7:56:24 PM12/1/17
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Bug | Status: closed
Component: Migrations | Version: master
Severity: Release blocker | Resolution: fixed
Keywords: mysql, | Triage Stage: Accepted
onetoonefield, utf8mb4, foreign |
key |
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 1

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Tim Graham <timograham@…>):

In [changeset:"b8a2f3c2d66aa15af4be745a576609b958a853c0" b8a2f3c]:
{{{
#!CommitTicketReference repository=""
revision="b8a2f3c2d66aa15af4be745a576609b958a853c0"
[1.11.x] Fixed #28305 -- Fixed "Cannot change column 'x': used in a
foreign key constraint" crash on MySQL with a sequence of AlterField or
RenameField operations.

Regression in 45ded053b1f4320284aa5dac63052f6d1baefea9.

Backport of c3e0adcad8d8ba94b33cabd137056166ed36dae0 from master
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:15>

Django

unread,
Sep 23, 2018, 7:52:16 PM9/23/18
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Bug | Status: closed
Component: Migrations | Version: master
Severity: Release blocker | Resolution: fixed
Keywords: mysql, | Triage Stage: Accepted
onetoonefield, utf8mb4, foreign |
key |
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 1

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Andrew Badr):

Replying to [comment:15 Tim Graham <timograham@…>]:


> In [changeset:"b8a2f3c2d66aa15af4be745a576609b958a853c0" b8a2f3c]:
> {{{
> #!CommitTicketReference repository=""
revision="b8a2f3c2d66aa15af4be745a576609b958a853c0"
> [1.11.x] Fixed #28305 -- Fixed "Cannot change column 'x': used in a
foreign key constraint" crash on MySQL with a sequence of AlterField or
RenameField operations.
>
> Regression in 45ded053b1f4320284aa5dac63052f6d1baefea9.
>
> Backport of c3e0adcad8d8ba94b33cabd137056166ed36dae0 from master
> }}}

This patch broke my project! See #29787

--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:16>

Django

unread,
Apr 10, 2020, 3:42:57 AM4/10/20
to django-...@googlegroups.com
#28305: AlterField migration tries to alter column that still has a foreign key
contraint
-------------------------------------+-------------------------------------
Reporter: Andreas Backx | Owner: nobody
Type: Bug | Status: closed
Component: Migrations | Version: master
Severity: Release blocker | Resolution: fixed
Keywords: mysql, | Triage Stage: Accepted
onetoonefield, utf8mb4, foreign |
key |
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 1

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Mariusz Felisiak <felisiak.mariusz@…>):

In [changeset:"f5ede1cb6da473166d22c04dcbd8240e2a0f223d" f5ede1cb]:
{{{
#!CommitTicketReference repository=""
revision="f5ede1cb6da473166d22c04dcbd8240e2a0f223d"
Refs #28305 -- Consolidated field referencing detection in migrations.

This moves all the field referencing resolution methods to shared
functions instead of duplicating efforts amongst state_forwards and
references methods.
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/28305#comment:17>

Reply all
Reply to author
Forward
0 new messages