我必须在两个不同的应用程序(如app1和app2)中使用django模型,在app1中,我得到了Model1,在app2中,我得到了BaseModel,而Model1就是这样

class Model1(BaseModel):
...


Model1和BaseModel在一个应用程序中,但是我将Model1移至app2,现在我也想将BaseModel也移至app2。
我的问题是,当我尝试将BaseModel移至app2时,出现以下错误:

Cannot resolve bases for [<ModelState: 'app1.model1'>]
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/2.1/topics/migrations/#dependencies for more


我的工作很简单:


我编写了将BaseModel表重命名为app2_basemodel的迁移,然后编写了在App2中创建模型的迁移
我创建迁移来更改用于继承的字段basemodel_ptr
我将BaseModel代码移动到app2并通过从app1迁移来删除BaseModel


此方法适用于移动Model1,但是当我尝试移动此基本模型时,出现此错误。

我非常感谢您提供的帮助,包括采取其他任何方法来实现将BaseModel移至app1的重构想法。

最佳答案

Django并未为此提供内置的迁移操作,但是您可以通过将多个迁移操作拼凑在一起来实现此目的。

迁移不仅会更改数据库,而且还会保留所有模型的状态。您必须确保状态与数据库保持同步,这会使事情变得有些复杂。密钥正在使用migrations.SeparateDatabaseAndState

假设您具有以下模型定义:

# app1/models.py
from django.db import models

class BaseModel(models.Model):
    base_field = models.CharField(max_length=64)

# app2/models.py
from django.db import models
from app1.models import BaseModel

class Model1(BaseModel):
    model_field = models.CharField(max_length=64)


您想迁移到此:

# app1/models.py empty
# app2/models.py

from django.db import models

class BaseModel(models.Model):
    base_field = models.CharField(max_length=64)

class Model1(BaseModel):
    model_field = models.CharField(max_length=64)


您必须创建三个迁移:


在App1中:将app1.BaseModel的表从app1_basemodel重命名为app2_basemodel。这还需要调整basemodel_ptr列上的外键约束。
在App2中:添加app2.BaseModel并使用app2.Model1作为基本模型重新创建app2.BaseModel。这些更改仅针对迁移状态完成,而不涉及数据库!
在App1中:从迁移状态中删除app1.BaseModel。同样,没有数据库更改。


这是代码中的样子:

# app1/migrations/0002_rename_basemodel_table.py
from django.db import migrations, models


class Migration(migrations.Migration):
    atomic = False
    dependencies = [
        ('app1', '0001_initial'),
    ]
    operations = [
        migrations.AlterModelTable(
            name='BaseModel',
            table='app2_basemodel'
        ),
    ]

# app2/migrations/0002_change_basemodel.py
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    dependencies = [
        ('app2', '0001_initial'),
        ('app1', '0002_rename_basemodel_table')
    ]

    state_operations = [
        migrations.CreateModel(
            name='BaseModel',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('base_field', models.CharField(max_length=64)),
            ],
        ),
        migrations.DeleteModel(
            name='Model1',
        ),
        migrations.CreateModel(
            name='Model1',
            fields=[
                ('basemodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='app2.BaseModel')),
            ],
            bases=('app2.basemodel',),
        ),
    ]
    database_operations = [
    ]
    operations = [
        migrations.SeparateDatabaseAndState(
            database_operations,
            state_operations
        )
    ]

# app1/0003_remove_basemodel.py
from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('app1', '0002_rename_basemodel_table'),
        ('app2', '0002_change_basemodel')
    ]

    state_operations = [
        migrations.DeleteModel(
            name='BaseModel',
        ),
    ]
    operations = [
        migrations.SeparateDatabaseAndState(
            database_operations=None,
            state_operations=state_operations
        )
    ]


显然,您必须调整这些迁移以反映您的实际模型。
恐怕如果您有其他与Model1有关系的模型,这可能会变得更加复杂。

免责声明:我已经使用SQLite和PostgreSQL对它们进行了测试,但使用风险自负!在生产数据上运行备份之前,请确保已备份。

之前:

$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
CREATE TABLE IF NOT EXISTS "app1_basemodel" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "base_field" varchar(64) NOT NULL);
CREATE TABLE IF NOT EXISTS "app2_model1" ("basemodel_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "app1_basemodel" ("id") DEFERRABLE INITIALLY DEFERRED, "model_field" varchar(64) NOT NULL);
...

$ python manage.py shell
Python 3.7.0 (default, Aug 22 2018, 15:22:33)
[Clang 9.1.0 (clang-902.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from app2.models import Model1
>>> Model1.__bases__
(<class 'app1.models.BaseModel'>,)
>>> Model1.objects.create(base_field='a', model_field='A')
<Model1: Model1 object (1)>
>>> Model1.objects.create(base_field='b', model_field='B')
<Model1: Model1 object (2)>
>>>


后:

sqlite> .schema
...
CREATE TABLE IF NOT EXISTS "app2_basemodel" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "base_field" varchar(64) NOT NULL);
CREATE TABLE IF NOT EXISTS "app2_model1" ("basemodel_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "app2_basemodel" ("id") DEFERRABLE INITIALLY DEFERRED, "model_field" varchar(64) NOT NULL);
...

>>> from app2.models import Model1
>>> Model1.__bases__
(<class 'app2.models.BaseModel'>,)
>>> for obj in Model1.objects.all():
...  print(obj.base_field, obj.model_field)
...
a A
b B


或者,您可以查看writing a custom migration operation

10-08 19:15