#33185: sqlmigrate crashes given a RenameModel operation with a self-referencing
foreign key on MySQL
---------------------------------------+------------------------
Reporter: Jacob Walls | Owner: nobody
Type: Bug | Status: new
Component: Migrations | Version: dev
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
---------------------------------------+------------------------
A migration that renames a table having a self-referencing foreign key
will migrate just fine but crashes when using `sqlmigrate` to inspect SQL
statements beforehand. I tested on MySQL 5.7.31.
1. fresh project `selfapp`
2. models.py -->
{{{
class Employee(models.Model):
manager = models.ForeignKey('self', on_delete=models.SET_NULL,
null=True)
}}}
3. `python manage.py makemigrations`
4. `python manage.py migrate`
5. models.py -->
{{{
class RenamedEmployee(models.Model):
manager = models.ForeignKey('self', on_delete=models.SET_NULL,
null=True)
}}}
6. `python manage.py makemigrations` answer prompt `Was the model
selfapp.Employee renamed to RenamedEmployee? [y/N] ` with Y
-- resulting migration: --
{{{
# Generated by Django 4.1.dev20211008135104 on 2021-10-10 16:11
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('selfapp', '0001_initial'),
]
operations = [
migrations.RenameModel(
old_name='Employee',
new_name='RenamedEmployee',
),
]
}}}
7. `python manage.py sqlmigrate selfapp 0002`
{{{
Traceback (most recent call last):
File "/Users/jwalls/django/django/db/backends/utils.py", line 82, in
_execute
return self.cursor.execute(sql)
File "/Users/jwalls/django/django/db/backends/mysql/base.py", line 73,
in execute
return self.cursor.execute(query, args)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9
/site-packages/MySQLdb/cursors.py", line 206, in execute
res = self._query(query)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9
/site-packages/MySQLdb/cursors.py", line 319, in _query
db.query(q)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9
/site-packages/MySQLdb/connections.py", line 259, in query
_mysql.connection.query(self, query)
MySQLdb._exceptions.ProgrammingError: (1146, "Table
'mydatabase.selfapp_renamedemployee' doesn't exist")
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/jwalls/self/manage.py", line 22, in <module>
main()
File "/Users/jwalls/self/manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "/Users/jwalls/django/django/core/management/__init__.py", line
419, in execute_from_command_line
utility.execute()
File "/Users/jwalls/django/django/core/management/__init__.py", line
413, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/Users/jwalls/django/django/core/management/base.py", line 363, in
run_from_argv
self.execute(*args, **cmd_options)
File
"/Users/jwalls/django/django/core/management/commands/sqlmigrate.py", line
29, in execute
return super().execute(*args, **options)
File "/Users/jwalls/django/django/core/management/base.py", line 407, in
execute
output = self.handle(*args, **options)
File
"/Users/jwalls/django/django/core/management/commands/sqlmigrate.py", line
65, in handle
sql_statements = loader.collect_sql(plan)
File "/Users/jwalls/django/django/db/migrations/loader.py", line 352, in
collect_sql
state = migration.apply(state, schema_editor, collect_sql=True)
File "/Users/jwalls/django/django/db/migrations/migration.py", line 125,
in apply
operation.database_forwards(self.app_label, schema_editor, old_state,
project_state)
File "/Users/jwalls/django/django/db/migrations/operations/models.py",
line 343, in database_forwards
schema_editor.alter_field(
File "/Users/jwalls/django/django/db/backends/base/schema.py", line 618,
in alter_field
self._alter_field(model, old_field, new_field, old_type, new_type,
File "/Users/jwalls/django/django/db/backends/base/schema.py", line 631,
in _alter_field
fk_names = self._constraint_names(model, [old_field.column],
foreign_key=True)
File "/Users/jwalls/django/django/db/backends/base/schema.py", line
1344, in _constraint_names
constraints = self.connection.introspection.get_constraints(cursor,
model._meta.db_table)
File "/Users/jwalls/django/django/db/backends/mysql/introspection.py",
line 287, in get_constraints
cursor.execute("SHOW INDEX FROM %s" %
self.connection.ops.quote_name(table_name))
File "/Users/jwalls/django/django/db/backends/utils.py", line 98, in
execute
return super().execute(sql, params)
File "/Users/jwalls/django/django/db/backends/utils.py", line 66, in
execute
return self._execute_with_wrappers(sql, params, many=False,
executor=self._execute)
File "/Users/jwalls/django/django/db/backends/utils.py", line 75, in
_execute_with_wrappers
return executor(sql, params, many, context)
File "/Users/jwalls/django/django/db/backends/utils.py", line 84, in
_execute
return self.cursor.execute(sql, params)
File "/Users/jwalls/django/django/db/utils.py", line 90, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/Users/jwalls/django/django/db/backends/utils.py", line 82, in
_execute
return self.cursor.execute(sql)
File "/Users/jwalls/django/django/db/backends/mysql/base.py", line 73,
in execute
return self.cursor.execute(query, args)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9
/site-packages/MySQLdb/cursors.py", line 206, in execute
res = self._query(query)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9
/site-packages/MySQLdb/cursors.py", line 319, in _query
db.query(q)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9
/site-packages/MySQLdb/connections.py", line 259, in query
_mysql.connection.query(self, query)
django.db.utils.ProgrammingError: (1146, "Table
'mydatabase.selfapp_renamedemployee' doesn't exist")
}}}
8. `python manage.py migrate` works fine:
{{{
Operations to perform:
Apply all migrations: admin, auth, contenttypes, selfapp, sessions
Running migrations:
Applying selfapp.0002_rename_employee_renamedemployee... OK
}}}
9. And now `python manage.py sqlmigrate selfapp 0002` works, but I wanted
to verify the safety of SQL (in a larger migration) before migrating:
{{{
--
-- Rename model Employee to RenamedEmployee
--
RENAME TABLE `selfapp_employee` TO `selfapp_renamedemployee`;
ALTER TABLE `selfapp_renamedemployee` DROP FOREIGN KEY
`selfapp_renamedemplo_manager_id_51e9d90c_fk_selfapp_r`;
ALTER TABLE `selfapp_renamedemployee` ADD CONSTRAINT
`selfapp_renamedemplo_manager_id_51e9d90c_fk_selfapp_r` FOREIGN KEY
(`manager_id`) REFERENCES `selfapp_renamedemployee` (`id`);
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/33185>
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 view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/058.e30cdf469b3e20fff1d0e181ef644b00%40djangoproject.com.