For some reasons I want to apply a major change on my models. I want to rework my whole design somehow, but the Django migration implementations keep in mind the previous design by not updating my models bases.
Let me show quickly what I had before then what I have now.
app1.TopLevel
|_ app1.IntermediateLevel
|_ app2.LowLevel
I had 3 models like those, then now I would like to cut off this design to something more suited to my current project, such as
app2.TopLevel
|_ app2.LowLevel
My major changes are, first I don't want an intermediate model anymore, second I don't need top keep the app1.TopLevel
that way.
I don't have issues with the data (I run multiple migration, some with Python to put the data into temporary fields, then put back the data in the right field later and remove those temporary fields).
My issue is, when we create an inherited model we define its bases ;
migrations.CreateModel(
name='IntermediateLevel',
fields=[
('toplevel_ptr', models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to='app1.TopLevel'),
)
],
bases=('app1.TopLevel',),
)
In that cases I would got something like
Local field 'toplevel_ptr' in class 'LowLevel' clashes with field of similar name from base class 'IntermediateLevel'`
I read the official documentation and the source code (for migrations) but so far I didn't see anything about it. Is it possible to tell to the migration system that we changed a model bases (its parents) ?
Otherwise, the only one solution I got would be to create new models, run python migrations to copy the data from the old models to the new ones. Then remove the old models and rename the new ones to get the names I want.
Had the same issue with the bases
and I ended up writing my own migration operation like below. However, worth mention that the whole process looks like follows:
IntegerField
to the model with null=True
xxx_ptr
to the new fieldxxx_ptr
fieldRemoveModelBasesOptions
operationid
and change it to AutoField
One thing to note is that if you have models with ForeignKey
to your model, it will preserve the link anyway, so it is safe.
class RemoveModelBasesOptions(ModelOptionOperation):
def __init__(self, name):
super().__init__(name)
def deconstruct(self):
kwargs = {
'name': self.name,
}
return (
self.__class__.__qualname__,
[],
kwargs
)
def state_forwards(self, app_label, state):
model_state = state.models[app_label, self.name_lower]
model_state.bases = (models.Model,)
state.reload_model(app_label, self.name_lower, delay=True)
def database_forwards(self, app_label, schema_editor, from_state,
to_state):
pass
def database_backwards(self, app_label, schema_editor, from_state,
to_state):
pass
def describe(self):
return "Remove bases from the model %s" % self.name
@property
def migration_name_fragment(self):
return 'remove_%s_bases' % self.name_lower
Then, simply call it as an operation in your migration:
class Migration(migrations.Migration):
dependencies = [
('xxxx', '0025_xxxx'),
]
operations = [
RemoveModelBasesOptions('Foo')
]
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