﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
36959	Model bases isn't updated when changing parent classes	Tim Schilling		"There appears to be a bug when removing multi-table inheritance. The majority of this ticket is covered in [https://www.better-simple.com/django/2025/03/19/model-bases-in-migrations/ this blog post], but I'm pulling out the relevant bits. If things don't make total sense, it's possible I removed too much context.

When removing multi-table inheritance by changing a child model's parent from a concrete model to an abstract model (or `models.Model` directly), `makemigrations` does not generate a migration operation to update the `bases` attribute in the migration state. 

**Steps to reproduce:**

1. Start with these models

{{{
class MyBaseModel(models.Model):
    value = models.IntegerField()

class MyChildModel(MyBaseModel):
    pass
}}}

Make migrations and apply them.

2. Change the models to:

{{{
class AbstractBase(models.Model):
    class Meta:
        abstract = True

    value = models.IntegerField(null=True)

class MyBaseModel(models.Model):
    pass

class MyChildModel(AbstractBase):
    pass
}}}

Make migrations and apply them. You'll get the error:
{{{
django.core.exceptions.FieldError: Local field 'id' in class 'MyChildModel' clashes with field of the same name from base class 'MyBaseModel'.
}}}

This is because there is no operation to update `bases` for `MyChildModel` from `(""myapp.mybasemodel"",)` to `(models.Model,)`.


**Expected Behavior**
`makemigrations` should detect that a model's bases have changed and generate an appropriate migration operation (similar to `AlterModelOptions`maybe?) to update `bases`.

**Workaround**
Thanks to [https://stackoverflow.com/a/67500550/1637351 Andrii on StackOverflow], there's a workaround for people to create their own operation to change the bases.

{{{
from django.db.migrations.operations.models import ModelOptionOperation

class SetModelBasesOptionOperation(ModelOptionOperation):
    """"""
    A migration operation that updates the bases of a model.
    This can be used to separate a model from its parent. Specifically
    when multi-table inheritance is used.
    """"""
    def __init__(self, name, bases):
        super().__init__(name)
        self.bases = bases

    def deconstruct(self):
        return (self.__class__.__qualname__, [], {""bases"": self.bases})

    def state_forwards(self, app_label, state):
        model_state = state.models[app_label, self.name_lower]
        model_state.bases = self.bases
        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 ""Update bases of the model %s"" % self.name

    @property
    def migration_name_fragment(self):
        return ""set_%s_bases"" % self.name_lower
}}}

Which would require the earlier migration's `operations` that fails to be updated to look like:

{{{ 
operations = [
    migrations.RemoveField(
        model_name='mybasemodel',
        name='value',
    ),
    migrations.RemoveField(
        model_name='mychildmodel',
        name='mybasemodel_ptr',
    ),
    SetModelBasesOptionOperation(""mychildmodel"", (models.Model, )),
    migrations.AddField(
        model_name='mychildmodel',
        name='id',
        field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
    ),
    migrations.AddField(
        model_name='mychildmodel',
        name='value',
        field=models.IntegerField(null=True),
    ),
]

}}}"	Bug	closed	Migrations		Normal	duplicate	mti		Unreviewed	0	0	0	0	0	0
