Lets say i have the following base model:
class human(models.Model):
gender = models.BooleanField()
age = models.IntegerField()
name = models.CharField(max_length=200)
And two models inheriting it:
class superhero(human):
can_fly = models.BooleanField()
class villain(human):
fingerprint = models.ImageField()
At some time in my development process i realized that i don't actually need the human class directly. I only need it to be a set of template parameters for superhero and villain models. If now i go to human Meta
class and set abstract=True
and change my models like so:
class human(models.Model):
gender = models.BooleanField()
age = models.IntegerField()
name = models.CharField(max_length=200)
class Meta:
abstract = True
class superhero(human):
can_fly = models.BooleanField()
class villain(human):
fingerprint = models.ImageField()
attempt to make migrations and migrate will raise the following error
Local field u'gender' in class 'superhero' clashes with field of similar name from base class 'human'
How can i switch to abstract class keeping all my migrations without tinkering the database directly?
Model inheritance. Model inheritance in Django works almost identically to the way normal class inheritance works in Python, but the basics at the beginning of the page should still be followed. That means the base class should subclass django.
To create an abstract class, just use the abstract keyword before the class keyword, in the class declaration. You can observe that except abstract methods the Employee class is same as normal class in Java. The class is now abstract, but it still has three fields, seven methods, and one constructor.
An abstract model is a base class in which you define fields you want to include in all child models. Django doesn't create any database table for abstract models. A database table is created for each child model, including the fields inherited from the abstract class and the ones defined in the child model.
The error was raised because of the way Django saves models to the database. All models that inherit from the base model human
don't have all human
fields in their own tables. Instead they have only their own fields and a foreign key that links them to the corresponding lines in human
table. But when you inherit from abstract class all the fields are saved directly to your model's table. So when I tried to change human
class to abstract=True
and inherit it in superhero
class Django tried to create all fields from human
table in superhero
table, which still has a foreign key to existing human entry with fields named exactly the same.
Following this instruction will make the desired result but unfortunately will destroy all entries of human
superhero
and villain
models
superhero
and villain
models so Django deletes themsuperhero
and villain
tables are deletedabstract=True
in human
classhuman
table because now it is an abstract classsuperhero
and villain
modelsvillain
and superhero
tables with all the fields from human
classThis is it.
P.S. Why I needed to move to abstract class? Because I wanted to make all my villains
and superheroes
unique using unique_together
parameter that makes some DB level restrictions. To make this possible all superhero
fields had to be in one table. Now it works.
This is an old question, but the answer saved me a lot of efforts. I just want to add something.
When making a model class to inherite from an abstract model, Django migrations will remove that model.
MyModel(models.Model):
# some fields to be inherited later from the abstract model
author = models.ForeignKey('auth.User')
# other fields specific to this model
Now if you create an abstract model:
MyAbstractModel(models.Model):
# fields to be used by children classes
class Meta:
abstract = True
and let your model inherit from it:
MyModel(MyAbstractModel):
author = models.ForeignKey('auth.User')
# other fields specific to this model
If you run makemigrations and migrate on your app Django will remove that model and delete the correspondent DB table.
You can outsmart Django by commenting out the code for that model. Django will remove it with all other consequences. Now you can uncomment the code and run the migrations again and Django will create the DB table again.
However you will likely have your admin, views, forms, etc., to import your model. This means the migrations will throw error, because that class can't be found. You'll have to comment out the code in all files where your model is imported.
Easier would be to skip the commenting out and write the wanted migration manually:
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
# app_label should be your app
# and 000x_the_last_migration is the name of the migration
# it depends on, usually the last one
('app_label', '000x_the_last_migration'),
]
operations = [
# do this for all ForeignKey, ManyToManyField, OneToOneField
# where model_name is obviously the model name and name is the
# field name
migrations.RemoveField(
model_name='mymodel',
name='author',
),
# finally delete the model
migrations.DeleteModel(
name='MyModel',
),
]
Now you can run the migration:
python manage.py migrate app_label
later inherit from the abstract model (see code above, 3rd block) and make new migration:
python manage.py makemigrations app_label
This should save you commenting out large chunks of code. If you want to save the data in the DB table, then you can use dumpdata
and loaddata
.
TLDR
This wasn't a step for step guide and requires experience with Django. In short you have to:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With