#30276: reverse DeleteModel migration with a primary key OneToOneField fails in
MySQL
-------------------------------------+-------------------------------------
               Reporter:  Florian    |          Owner:  nobody
  Zimmermann                         |
                   Type:             |         Status:  new
  Uncategorized                      |
              Component:             |        Version:  2.1
  Migrations                         |
               Severity:  Normal     |       Keywords:  migrations, mysql
           Triage Stage:             |      Has patch:  0
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 == Preparations ==

 In a new app create two models (without the `dummy` field this would
 trigger #27746):

 {{{#!python
 from django.db import models

 class One(models.Model):
     pass

 class Two(models.Model):
     one = models.OneToOneField(One, primary_key=True,
 on_delete=models.CASCADE)
     dummy = models.IntegerField()
 }}}

 `python manage.py makemigrations` yields this migration:

 {{{#!python
 from django.db import migrations, models
 import django.db.models.deletion

 class Migration(migrations.Migration):
     initial = True
     dependencies = []
     operations = [
         migrations.CreateModel(
             name='One',
             fields=[
                 ('id', models.AutoField(auto_created=True,
 primary_key=True, serialize=False, verbose_name='ID')),
             ],
         ),
         migrations.CreateModel(
             name='Two',
             fields=[
                 ('one',
 models.OneToOneField(on_delete=django.db.models.deletion.CASCADE,
 primary_key=True, serialize=False, to='bug.One')),
                 ('dummy', models.IntegerField()),
             ],
         ),
     ]

 }}}

 Then remove the `Two` model:
 {{{#!python
 from django.db import models

 class One(models.Model):
     pass

 #class Two(models.Model):
 #    one = models.OneToOneField(One, primary_key=True,
 on_delete=models.CASCADE)
 #    dummy = models.IntegerField()
 }}}

 which yields the offending migration:
 {{{#!python
 from django.db import migrations

 class Migration(migrations.Migration):
     dependencies = [
         ('bug', '0001_initial'),
     ]
     operations = [
         migrations.RemoveField(
             model_name='two',
             name='one',
         ),
         migrations.DeleteModel(
             name='Two',
         ),
     ]
 }}}

 == Triggering the bug ==

 Apply both migrations and then unapply the second migration:
 {{{
 python manage.py migrate bug
 python manage.py migrate bug 0001
 }}}
 {{{
 Operations to perform:
   Target specific migration: 0001_initial, from bug
 Running migrations:
   Rendering model states... DONE
   Unapplying bug.0002_auto_20190321_1219...Traceback (most recent call
 last):
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\db\backends\utils.py", line 85, in _execute
     return self.cursor.execute(sql, params)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\db\backends\mysql\base.py", line 71, in execute
     return self.cursor.execute(query, args)
   File "D:\temp\django-bug\venv\lib\site-packages\MySQLdb\cursors.py",
 line 250, in execute
     self.errorhandler(self, exc, value)
   File "D:\temp\django-bug\venv\lib\site-packages\MySQLdb\connections.py",
 line 50, in defaulterrorhandler
     raise errorvalue
   File "D:\temp\django-bug\venv\lib\site-packages\MySQLdb\cursors.py",
 line 247, in execute
     res = self._query(query)
   File "D:\temp\django-bug\venv\lib\site-packages\MySQLdb\cursors.py",
 line 412, in _query
     rowcount = self._do_query(q)
   File "D:\temp\django-bug\venv\lib\site-packages\MySQLdb\cursors.py",
 line 375, in _do_query
     db.query(q)
   File "D:\temp\django-bug\venv\lib\site-packages\MySQLdb\connections.py",
 line 276, in query
     _mysql.connection.query(self, query)
 _mysql_exceptions.OperationalError: (1068, 'Multiple primary key defined')

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

 Traceback (most recent call last):
   File "manage.py", line 15, in <module>
     execute_from_command_line(sys.argv)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\core\management\__init__.py", line 381, in
 execute_from_command_line
     utility.execute()
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\core\management\__init__.py", line 375, in execute
     self.fetch_command(subcommand).run_from_argv(self.argv)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\core\management\base.py", line 316, in run_from_argv
     self.execute(*args, **cmd_options)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\core\management\base.py", line 353, in execute
     output = self.handle(*args, **options)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\core\management\base.py", line 83, in wrapped
     res = handle_func(*args, **kwargs)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\core\management\commands\migrate.py", line 203, in handle
     fake_initial=fake_initial,
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\db\migrations\executor.py", line 121, in migrate
     state = self._migrate_all_backwards(plan, full_plan, fake=fake)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\db\migrations\executor.py", line 196, in
 _migrate_all_backwards
     self.unapply_migration(states[migration], migration, fake=fake)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\db\migrations\executor.py", line 262, in unapply_migration
     state = migration.unapply(state, schema_editor)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\db\migrations\migration.py", line 175, in unapply
     operation.database_backwards(self.app_label, schema_editor,
 from_state, to_state)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\db\migrations\operations\fields.py", line 156, in
 database_backwards
     schema_editor.add_field(from_model,
 to_model._meta.get_field(self.name))
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\db\backends\mysql\schema.py", line 42, in add_field
     super().add_field(model, field)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\db\backends\base\schema.py", line 435, in add_field
     self.execute(sql, params)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\db\backends\base\schema.py", line 133, in execute
     cursor.execute(sql, params)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\db\backends\utils.py", line 100, in execute
     return super().execute(sql, params)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\db\backends\utils.py", line 68, in execute
     return self._execute_with_wrappers(sql, params, many=False,
 executor=self._execute)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\db\backends\utils.py", line 77, in _execute_with_wrappers
     return executor(sql, params, many, context)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\db\backends\utils.py", line 85, in _execute
     return self.cursor.execute(sql, params)
   File "D:\temp\django-bug\venv\lib\site-packages\django\db\utils.py",
 line 89, in __exit__
     raise dj_exc_value.with_traceback(traceback) from exc_value
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\db\backends\utils.py", line 85, in _execute
     return self.cursor.execute(sql, params)
   File "D:\temp\django-bug\venv\lib\site-
 packages\django\db\backends\mysql\base.py", line 71, in execute
     return self.cursor.execute(query, args)
   File "D:\temp\django-bug\venv\lib\site-packages\MySQLdb\cursors.py",
 line 250, in execute
     self.errorhandler(self, exc, value)
   File "D:\temp\django-bug\venv\lib\site-packages\MySQLdb\connections.py",
 line 50, in defaulterrorhandler
     raise errorvalue
   File "D:\temp\django-bug\venv\lib\site-packages\MySQLdb\cursors.py",
 line 247, in execute
     res = self._query(query)
   File "D:\temp\django-bug\venv\lib\site-packages\MySQLdb\cursors.py",
 line 412, in _query
     rowcount = self._do_query(q)
   File "D:\temp\django-bug\venv\lib\site-packages\MySQLdb\cursors.py",
 line 375, in _do_query
     db.query(q)
   File "D:\temp\django-bug\venv\lib\site-packages\MySQLdb\connections.py",
 line 276, in query
     _mysql.connection.query(self, query)
 django.db.utils.OperationalError: (1068, 'Multiple primary key defined')
 }}}

 And sure enough the SQL for the backwards migration looks like this
 (`python manage.py sqlmigrate --backwards bug 0002`):
 {{{#!sql
 BEGIN;
 --
 -- Delete model Two
 --
 CREATE TABLE `bug_two` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
 `foo` integer NOT NULL);
 --
 -- Remove field one from two
 --
 ALTER TABLE `bug_two` ADD COLUMN `one_id` integer NOT NULL PRIMARY KEY;
 ALTER TABLE `bug_two` ADD CONSTRAINT
 `bug_two_one_id_2a65406f_fk_bug_one_id` FOREIGN KEY (`one_id`) REFERENCES
 `bug_one` (`id`);
 COMMIT;
 }}}

-- 
Ticket URL: <https://code.djangoproject.com/ticket/30276>
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/052.bb32ef9eb0edef830ec6907db184a182%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to