You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

解决Django迁移时InvalidBasesError无法解析基类的问题

Django迁移InvalidBasesError问题解决

问题背景

我有coreapp和product两个Django应用,最初为复用公共字段,在coreapp中创建了BaseModel,product的所有模型都继承该类。执行python manage.py makemigrations和migrate完成初始迁移后,做了两处修改:

  • 在BaseModel中添加abstract = True,将其改为抽象基类
  • 把product模型中的attribute_object重命名为attribute、category_object重命名为category

再次执行makemigrations生成新迁移文件后,运行migrate时出现以下错误:

django.db.migrations.exceptions.InvalidBasesError: Cannot resolve bases for [<ModelState: 'product.Attribute'>, <ModelState: 'product.Category'>, <ModelState: 'product.Product'>, <ModelState: 'product.AttributeValue'>]
This can happen if you are inheriting models from an app with migrations (e.g. contrib.auth) in an app with no migrations; see https://docs.djangoproject.com/en/4.1/topics/migrations/#dependencies for more

相关代码

coreapp/models.py(修改前)

from django.db import models

class BaseModel(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

coreapp/models.py(修改后)

from django.db import models

class BaseModel(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        abstract = True

product/models.py(修改前)

from django.db import models
from project_root.coreapp.models import BaseModel

class Attribute(BaseModel):
    title = models.CharField(max_length=100)
    def __str__(self):
        return self.title
    
class AttributeValue(BaseModel):
    title = models.CharField(max_length=100)
    attribute_object = models.ForeignKey(Attribute,on_delete=models.CASCADE, related_name='attribute_values')
    def __str__(self):
        return self.title

class Category(BaseModel):
    title = models.CharField(max_length=200)
    parent= models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)
    def __str__(self):
        return self.title

class Product(BaseModel):
    title = models.CharField(max_length=500)
    price = models.FloatField()
    qty = models.BigIntegerField()
    sku = models.CharField(max_length=100)
    detail= models.TextField(max_length=5000)
    category_object = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='products')
    def __str__(self):
        return self.title

product/models.py(修改后)

from django.db import models
from project_root.coreapp.models import BaseModel

class Attribute(BaseModel):
    title = models.CharField(max_length=100)
    def __str__(self):
        return self.title
    
class AttributeValue(BaseModel):
    title = models.CharField(max_length=100)
    attribute = models.ForeignKey(Attribute,on_delete=models.CASCADE, related_name='attribute_values')
    def __str__(self):
        return self.title

class Category(BaseModel):
    title = models.CharField(max_length=200)
    parent= models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)
    def __str__(self):
        return self.title

class Product(BaseModel):
    title = models.CharField(max_length=500)
    price = models.FloatField()
    qty = models.BigIntegerField()
    sku = models.CharField(max_length=100)
    detail= models.TextField(max_length=5000)
    category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='products')
    def __str__(self):
        return self.title

生成的迁移文件

coreapp/migrations/0001_initial.py

from django.db import migrations, models

class Migration(migrations.Migration):
    initial = True
    dependencies = []
    operations = [
        migrations.CreateModel(
            name='BaseModel',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('created_at', models.DateTimeField(auto_now_add=True)),
                ('updated_at', models.DateTimeField(auto_now=True)),
            ],
        ),
    ]

product/migrations/0001_initial.py

# Generated by Django 4.1 on 2023-10-15 05:59
from django.db import migrations, models
import django.db.models.deletion

class Migration(migrations.Migration):
    initial = True
    dependencies = [('coreapp', '0001_initial')]
    operations = [
        migrations.CreateModel(
            name='Attribute',
            fields=[
                ('basemodel_ptr', models.OneToOneField(auto_created=True, 
                                                    on_delete=django.db.models.deletion.CASCADE, 
                                                    parent_link=True, primary_key=True, serialize=False,
                                                    to='coreapp.basemodel')),
                ('title', models.CharField(max_length=100)),
            ],
            bases=('coreapp.basemodel',),
        ),
        migrations.CreateModel(
            name='Category',
            fields=[
                ('basemodel_ptr', models.OneToOneField(auto_created=True, 
                                                    on_delete=django.db.models.deletion.CASCADE, 
                                                parent_link=True, primary_key=True, serialize=False, 
                                                to='coreapp.basemodel')),
                ('title', models.CharField(max_length=200)),
                ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, 
                                            to='product.category')),
            ],
            bases=('coreapp.basemodel',),
        ),
        migrations.CreateModel(
            name='Product',
            fields=[
                ('basemodel_ptr', models.OneToOneField(auto_created=True, 
                                                    on_delete=django.db.models.deletion.CASCADE, 
                                                    parent_link=True, primary_key=True, serialize=False, 
                                                    to='coreapp.basemodel')),
                ('title', models.CharField(max_length=500)),
                ('price', models.FloatField()),
                ('qty', models.BigIntegerField()),
                ('sku', models.CharField(max_length=100)),
                ('detail', models.TextField(max_length=5000)),
                ('category_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 
                                                    related_name='products', to='product.category')),
            ],
            bases=('coreapp.basemodel',),
        ),
        migrations.CreateModel(
            name='AttributeValue',
            fields=[
                ('basemodel_ptr', models.OneToOneField(auto_created=True, 
                                                    on_delete=django.db.models.deletion.CASCADE, 
                                                    parent_link=True, primary_key=True, serialize=False, 
                                                    to='coreapp.basemodel')),
                ('title', models.CharField(max_length=100)),
                ('attribute_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 
                                                    related_name='attribute_values', to='product.attribute')),
            ],
            bases=('coreapp.basemodel',),
        ),
    ]
print('product migration 1')

修改后自动生成的错误迁移文件

coreapp/migrations/0002_delete_basemodel.py

# Generated by Django 4.1 on 2023-10-15 06:03
from django.db import migrations

class Migration(migrations.Migration):
    dependencies = [
        ('product', '0002_rename_attribute_object_attributevalue_attribute_and_more'),
        ('coreapp', '0001_initial'),
    ]
    operations = [migrations.DeleteModel(name='BaseModel')]

product/migrations/0002_rename_attribute_object_attributevalue_attribute_and_more.py

# Generated by Django 4.1 on 2023-10-15 06:03
from django.db import migrations, models
import django.utils.timezone

class Migration(migrations.Migration):
    dependencies = [('product', '0001_initial')]
    operations = [
        migrations.RenameField(
            model_name='attributevalue',
            old_name='attribute_object',
            new_name='attribute',
        ),
        migrations.RenameField(
            model_name='product',
            old_name='category_object',
            new_name='category',
        ),
        migrations.RemoveField(model_name='attribute', name='basemodel_ptr'),
        migrations.RemoveField(model_name='attributevalue', name='basemodel_ptr'),
        migrations.RemoveField(model_name='category', name='basemodel_ptr'),
        migrations.RemoveField(model_name='product', name='basemodel_ptr'),
        migrations.AddField(
            model_name='attribute',
            name='created_at',
            field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
            preserve_default=False,
        ),
        migrations.AddField(
            model_name='attribute',
            name='id',
            field=models.BigAutoField(auto_created=True, default=1, primary_key=True, serialize=False, 
                                    verbose_name='ID'),
            preserve_default=False,
        ),
        migrations.AddField(model_name='attribute', name='updated_at', field=models.DateTimeField(auto_now=True)),
        migrations.AddField(
            model_name='attributevalue',
            name='created_at',
            field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
            preserve_default=False,
        ),
        migrations.AddField(
            model_name='attributevalue',
            name='id',
            field=models.BigAutoField(auto_created=True, default=1, primary_key=True, serialize=False, 
                                    verbose_name='ID'),
            preserve_default=False,
        ),
        migrations.AddField(model_name='attributevalue', name='updated_at', field=models.DateTimeField(auto_now=True)),
        migrations.AddField(
            model_name='category',
            name='created_at',
            field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
            preserve_default=False,
        ),
        migrations.AddField(
            model_name='category',
            name='id',
            field=models.BigAutoField(auto_created=True, default=1, primary_key=True, serialize=False, 
                                    verbose_name='ID'),
            preserve_default=False,
        ),
        migrations.AddField(model_name='category', name='updated_at', field=models.DateTimeField(auto_now=True)),
        migrations.AddField(
            model_name='product',
            name='created_at',
            field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
            preserve_default=False,
        ),
        migrations.AddField(
            model_name='product',
            name='id',
            field=models.BigAutoField(auto_created=True, default=1, primary_key=True, serialize=False, 
                                    verbose_name='ID'),
            preserve_default=False,
        ),
        migrations.AddField(model_name='product', name='updated_at', field=models.DateTimeField(auto_now=True)),
    ]

print('product migration 2')

解决方案

问题核心是:将非抽象模型改为抽象模型时,Django自动生成的迁移文件存在依赖顺序冲突,继承关系变更的迁移逻辑不顺畅。可按以下步骤解决:

步骤1:回滚到初始迁移状态

保留修改后的模型代码,将数据库回滚到第一次迁移完成的状态:

python manage.py migrate coreapp 0001
python manage.py migrate product 0001

步骤2:删除自动生成的错误迁移文件

删除coreapp/migrations/0002_delete_basemodel.py和product/migrations/0002_rename_attribute_object_attributevalue_attribute_and_more.py这两个文件。

步骤3:分阶段处理迁移

第一阶段:仅处理字段重命名

暂时保留BaseModel为非抽象类,只提交字段名修改的迁移:

python manage.py makemigrations product
python manage.py migrate product

此时会生成仅包含字段重命名的迁移文件(比如product/migrations/0002_rename_fields.py)。

第二阶段:处理BaseModel改为抽象类

  1. 确认coreapp/models.py中已添加abstract=True。
  2. 在coreapp/migrations下创建0002_make_basemodel_abstract.py:
from django.db import migrations

class Migration(migrations.Migration):
    dependencies = [
        ('coreapp', '0001_initial'),
        ('product', '0002_rename_fields'),  # 替换为实际生成的字段重命名迁移文件名
    ]

    operations = [migrations.DeleteModel(name='BaseModel')]
  1. 在product/migrations下创建0003_convert_to_abstract_base.py:
from django.db import migrations, models

class Migration(migrations.Migration):
    dependencies = [
        ('product', '0002_rename_fields'),  # 替换为实际生成的字段重命名迁移文件名
        ('coreapp', '0002_make_basemodel_abstract'),
    ]

    operations = [
        # 移除继承关联字段
        migrations.RemoveField(model_name='attribute', name='basemodel_ptr'),
        migrations.RemoveField(model_name='attributevalue', name='basemodel_ptr'),
        migrations.RemoveField(model_name='category', name='basemodel_ptr'),
        migrations.RemoveField(model_name='product', name='basemodel_ptr'),
        # 添加抽象基类的公共字段到每个模型
        migrations.AddField(model_name='attribute', name='created_at', field=models.DateTimeField(auto_now_add=True)),
        migrations.AddField(model_name='attribute', name='updated_at', field=models.DateTimeField(auto_now=True)),
        migrations.AddField(model_name='attribute', name='id', field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), preserve_default=False),
        
        migrations.AddField(model_name='attributevalue', name='created_at', field=models.DateTimeField(auto_now_add=True)),
        migrations.AddField(model_name='attributevalue', name='updated_at', field=models.DateTimeField(auto_now=True)),
        migrations.AddField(model_name='attributevalue', name='id', field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), preserve_default=False),
        
        migrations.AddField(model_name='category', name='created_at', field=models.DateTimeField(auto_now_add=True)),
        migrations.AddField(model_name='category', name='updated_at', field=models.DateTimeField(auto_now=True)),
        migrations.AddField(model_name='category', name='id', field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), preserve_default=False),
        
        migrations.AddField(model_name='product', name='created_at', field=models.DateTimeField(auto_now_add=True)),
        migrations.AddField(model_name='product', name='updated_at', field=models.DateTimeField(auto_now=True)),
        migrations.AddField(model_name='product', name='id', field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), preserve_default=False),
    ]

步骤4:执行最终迁移

按顺序执行迁移:

python manage.py migrate coreapp
python manage.py migrate product

替代方案(开发环境适用)

如果是开发环境且数据可丢弃,可直接重置:

  1. 删除coreapp/migrations和product/migrations下除__init__.py外的所有文件
  2. 删除数据库文件(如sqlite的db.sqlite3)
  3. 重新初始化迁移:
python manage.py makemigrations
python manage.py migrate

内容的提问来源于stack exchange,提问作者Tasif

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 21:40:54