#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 []) 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; }}} {{{ $ 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 <https://code.djangoproject.com/> The Web framework for perfectionists with deadlines. -- You received this message because you are subscribed to the Google Groups "Django updates" group. To unsubscribe from this group and stop receiving emails from it, send an email to django-updates+unsubscr...@googlegroups.com. To post to this group, send email to django-updates@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-updates/070.7b109893c949b40f06054fa4a112bc3b%40djangoproject.com. For more options, visit https://groups.google.com/d/optout.