Reputation: 535
I'm using Django 3.0.4 with MySQL 5.7 on Ubuntu 18.04.
I have a model Apples
. I created a second model BadApples
as a subclass of it:
# models.py
class Apples(models.Model):
# fields
class BadApples(Apples):
pass
I ran the migration, which completed successfully.
Then, I decided that the 'subclass' approach wasn't working and that BadApples
should be its own model. I rewrote the models like this:
# models.py
class Apples(models.Model):
# fields
class BadApples(models.Model):
# fields
When I tried to run the migration, I ran into the following error:
MySQLdb._exceptions.OperationalError: (1090, "You can't delete all columns with ALTER TABLE; use DROP TABLE instead")
As best I can tell, migrating BadApples
from one form to the other involves changing all of its columns. Instead of dropping the table and recreating it, Django uses ALTER TABLE commands only, which throws the MySQL error when it attempts to remove the last column of the original BadApples
. This seems related to this bug, purportedly fixed over two years ago, but evidently not fully.
To work around this bug, my idea was to remove BadApples
from models.py
and all references to BadApples
from the rest of the code (in this case, views.py
and admin.py
). Then I'd run a migration, which would drop the BadApples
table entirely because it no longer exists, and then I could recreate it as a separate migration.
Instead, after confirming that there is no trace of the BadApples
model anywhere in my code, running makemigrations
throws this error:
django.core.exceptions.FieldError: Local field 'id' in class 'BadApples' clashes with field of the same name from base class 'Apples'.
When I originally created BadApples
as a child of Apples
, its table had no id
field because its data was stored in the Apples
table. After I changed it to be an independent model, its table now gets assigned an id
field. Django retains some memory of BadApples
being a child of Apples
, however, and it sees this as a conflict.
To try to fix this, I reverted to the migration immediately before I ever created BadApples
in the first place. This migration completed successfully:
$ python3 manage.py migrate AppName 0012_auto_some_number
Operations to perform:
Target specific migration: 0012_auto_some_number, from AppName
Running migrations:
Rendering model states... DONE
Unapplying AppName.0013_badapples... OK
At this point, there is NO reference to BadApples
anywhere in my code, and the models are at a point where BadApples
has never even existed. Yet, when I try to run a migration (with no further changes to models.py
), I still get the error
django.core.exceptions.FieldError: Local field 'id' in class 'BadApples' clashes with field of the same name from base class 'Apples'.
Django is fixated on the idea that BadApples
used to exist and be a subclass of Apples
. How do I un-fixate it?
Upvotes: 0
Views: 729
Reputation: 91555
This type of operation you're trying to perform is sufficiently complicated that Django Migrate can't infer what you're trying to do. In situations like these you should write a custom migration to handle how it can migrate the database forward. In this migration you can tell it to properly drop the table and create a new one.
In the Migration Files topic there is an example that shows the migrations.DeleteModel()
method.
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [('migrations', '0001_initial')]
operations = [
migrations.DeleteModel('Tribble'),
migrations.AddField('Author', 'rating', models.IntegerField(default=0)),
]
Upvotes: 1