#27746: Database migration fail when removing a child model containing only
foreignkeys in a multi-table inheritance context on MySQL
-------------------------------------+-------------------------------------
               Reporter:  David      |          Owner:  nobody
  CHANIAL                            |
                   Type:  Bug        |         Status:  new
              Component:  Database   |        Version:  1.9
  layer (models, ORM)                |
               Severity:  Normal     |       Keywords:
           Triage Stage:             |      Has patch:  0
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 == Context

 * I'm not observing this bug when not using '''multi-table inheritance'''.
 * I'm using '''MySQL''' (''MariaDB-10.0''), and i don't know if the bug
 happen with another database backend or server.
 * In an existing project named '''bobb'''
 * A similary bug exist with empty child models too, like '''Account''' or
 '''Device'''


 == Prepare the bug

 {{{
 python manage.py startapp buggymigration
 echo "INSTALLED_APPS = tuple(list(INSTALLED_APPS) + ['buggymigration'])"
 >> bobb/settings.py

 # creating models using multi-table in heritance in buggymigration
 # by making changes to buggymigration/models.py

 cat buggymigration/models.py
 }}}

 {{{
 #!python
 from __future__ import unicode_literals

 from django.db import models

 class UniqueSerialNumber(models.Model):
     pass

 class Account(UniqueSerialNumber):
     pass

 class Device(UniqueSerialNumber):
     pass

 class Contract(UniqueSerialNumber):
     customer = models.ForeignKey('Account')
     machine = models.ForeignKey('Device')
 }}}

 {{{
 python manage.py makemigrations
 }}}

 {{{
 Migrations for 'buggymigration':
   0001_initial.py:
     - Create model UniqueSerialNumber
     - Create model Account
     - Create model Contract
     - Create model Device
     - Add field machine to contract
 }}}

 {{{
 python manage.py migrate
 }}}

 {{{
 Operations to perform:
   Apply all migrations: sessions, admin, auth, buggymigration,
 contenttypes
 Running migrations:
   Applying buggymigration.0001_initial... OK
 }}}


 == Trigger the bug

 {{{
 # trying to remove the model Contract
 # by editing buggymigration/models.py and commenting the model

 cat buggymigration/models.py
 }}}

 {{{
 #!python
 from __future__ import unicode_literals

 from django.db import models

 class UniqueSerialNumber(models.Model):
     pass

 class Account(UniqueSerialNumber):
     pass

 class Device(UniqueSerialNumber):
     pass

 '''
 class Contract(UniqueSerialNumber):
     customer = models.ForeignKey('Account')
     machine = models.ForeignKey('Device')
 '''
 }}}

 {{{
 python manage.py makemigrations
 }}}
 {{{
 Migrations for 'buggymigration':
   0002_auto_20170118_1258.py:
     - Remove field customer from contract
     - Remove field machine from contract
     - Remove field uniqueserialnumber_ptr from contract
     - Delete model Contract
 }}}

 {{{
 cat buggymigration/migrations/0002_auto_20170118_1258.py
 }}}

 {{{
 #!python
 # -*- coding: utf-8 -*-
 # Generated by Django 1.9.12 on 2017-01-18 11:58
 from __future__ import unicode_literals

 from django.db import migrations


 class Migration(migrations.Migration):

     dependencies = [
         ('buggymigration', '0001_initial'),
     ]

     operations = [
         migrations.RemoveField(
             model_name='contract',
             name='customer',
         ),
         migrations.RemoveField(
             model_name='contract',
             name='machine',
         ),
         migrations.RemoveField(
             model_name='contract',
             name='uniqueserialnumber_ptr',
         ),
         migrations.DeleteModel(
             name='Contract',
         ),
     ]
 }}}

 {{{
 python manage.py sqlmigrate buggymigration 0002_auto_20170118_1258
 }}}

 {{{
 #!sql
 BEGIN;
 --
 -- Remove field customer from contract
 --
 ALTER TABLE `buggymigration_contract` DROP FOREIGN KEY
 `D5e39daefe3dcc06188b9ecc2ad1e28a`;
 ALTER TABLE `buggymigration_contract` DROP COLUMN `customer_id` CASCADE;
 --
 -- Remove field machine from contract
 --
 ALTER TABLE `buggymigration_contract` DROP FOREIGN KEY
 `a46c0a1038d68a3574efaf2d757512fd`;
 ALTER TABLE `buggymigration_contract` DROP COLUMN `machine_id` CASCADE;
 --
 -- Remove field uniqueserialnumber_ptr from contract
 --
 ALTER TABLE `buggymigration_contract` DROP FOREIGN KEY
 `f406fa7acbef3134feeae510d6e9affd`;
 ALTER TABLE `buggymigration_contract` DROP COLUMN
 `uniqueserialnumber_ptr_id` CASCADE;
 --
 -- Delete model Contract
 --
 DROP TABLE `buggymigration_contract` CASCADE;

 COMMIT;
 }}}


 == The bug

 {{{
 python manage.py migrate buggymigration
 }}}

 {{{
 Operations to perform:
   Apply all migrations: buggymigration
 Running migrations:
   Applying buggymigration.0002_auto_20170118_1258...Traceback (most recent
 call last):
   File "manage.py", line 10, in <module>
     execute_from_command_line(sys.argv)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/core/management/__init__.py", line 353, in
 execute_from_command_line
     utility.execute()
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/core/management/__init__.py", line 345, in execute
     self.fetch_command(subcommand).run_from_argv(self.argv)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/core/management/base.py", line 348, in run_from_argv
     self.execute(*args, **cmd_options)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/core/management/base.py", line 399, in execute
     output = self.handle(*args, **options)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/core/management/commands/migrate.py", line 200, in handle
     executor.migrate(targets, plan, fake=fake, fake_initial=fake_initial)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/db/migrations/executor.py", line 92, in migrate
     self._migrate_all_forwards(plan, full_plan, fake=fake,
 fake_initial=fake_initial)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/db/migrations/executor.py", line 121, in
 _migrate_all_forwards
     state = self.apply_migration(state, migration, fake=fake,
 fake_initial=fake_initial)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/db/migrations/executor.py", line 198, in apply_migration
     state = migration.apply(state, schema_editor)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/db/migrations/migration.py", line 123, in apply
     operation.database_forwards(self.app_label, schema_editor, old_state,
 project_state)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/db/migrations/operations/fields.py", line 121, in
 database_forwards
     schema_editor.remove_field(from_model,
 from_model._meta.get_field(self.name))
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/db/backends/base/schema.py", line 438, in remove_field
     self.execute(sql)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/db/backends/base/schema.py", line 110, in execute
     cursor.execute(sql, params)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/db/backends/utils.py", line 79, in execute
     return super(CursorDebugWrapper, self).execute(sql, params)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/db/backends/utils.py", line 64, in execute
     return self.cursor.execute(sql, params)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/db/utils.py", line 95, in __exit__
     six.reraise(dj_exc_type, dj_exc_value, traceback)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/db/backends/utils.py", line 64, in execute
     return self.cursor.execute(sql, params)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/django/db/backends/mysql/base.py", line 112, in execute
     return self.cursor.execute(query, args)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/MySQLdb/cursors.py", line 205, in execute
     self.errorhandler(self, exc, value)
   File "/home/backoffice/virtualenv/local/lib/python2.7/site-
 packages/MySQLdb/connections.py", line 36, in defaulterrorhandler
     raise errorclass, errorvalue
 django.db.utils.OperationalError: (1090, "You can't delete all columns
 with ALTER TABLE; use DROP TABLE instead")
 }}}

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

Reply via email to