#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 [email protected].
To post to this group, send email to [email protected].
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.

Reply via email to