无法使用 Django 3.0.3 中的迁移 API 使用 ModelState 和 ProjectState 进行迁移

Posted

技术标签:

【中文标题】无法使用 Django 3.0.3 中的迁移 API 使用 ModelState 和 ProjectState 进行迁移【英文标题】:Unable to migrate using ModelState and ProjectState using of migrations API in Django 3.0.3 【发布时间】:2021-02-14 18:27:05 【问题描述】:

我正在使用 ProjectState 迁移到表的新属性。我正在尝试了解在 Django 3.0.3 中使用迁移 API 的 ModelState 和 ProjectState。

我无法迁移到具有新字段的新州。有人可以帮助我使用 ProjectStateModelState 使用什么来申请新的 model_definition 迁移工作吗?以下代码不会迁移到数据库,但不会出现任何错误。

我想从一个数据库表状态迁移到另一个状态并且有一些元数据_meta

当前DB状态model_state.fields是:

[('id', <django.db.models.fields.AutoField>)]

添加fields_attrs 迁移后的未来数据库状态model_state.fields 应该是使用models_definition

[('id', <django.db.models.fields.AutoField>), ('name', <django.db.models.fields.CharField>)]

模型定义代码为:

model_config 对象是


 '__module__': 'testmodule', 'app_label': 'testmodule', 
 '__unicode__': <function ModelScript.model_create_config.<locals>.<lambda> at 0x00000221B6FBEF70>, 
 'attrs': 'name': <django.db.models.fields.CharField>

model_definition 是:

model_definition = type(
                model_item.table_name,
                # TODO: Put this into Database
                # model_config.get("extends"),
                bases,
                model_config
            )

这是我正在使用的代码:

from django.db.migrations.state import ProjectState
from django.db.migrations.migration import Migration
from django.db.migrations.state import ModelState
from django.db.migrations import operations

# model_definition is coming from a function as the following object
model_definition = '__module__': 'testmodule', 'app_label': 'testmodule', '__unicode__': <function ModelScript.model_create_config.<locals>.<lambda> at 0x000002047275FF70>, 'attrs': 'name': <django.db.models.fields.CharField>, '__doc__': 'SampleModel(id)', '_meta': <Options for SampleModel>, 'DoesNotExist': <class 'testmodule.SampleModel.DoesNotExist'>, 'MultipleObjectsReturned': <class 'testmodule.SampleModel.MultipleObjectsReturned'>, 'id': <django.db.models.query_utils.DeferredAttribute object at 0x00000204727F9430>, 'objects': <django.db.models.manager.ManagerDescriptor object at 0x00000204727F9490>

model_state = ModelState.from_model(model_definition)

# field_attrs are all the new fields to be migrated         
for k,v in field_attrs.items():
    model_state.fields.append((k, v))

# Create a fake migration with the CreateModel operation
cm = operations.CreateModel(name=model_state.name, fields=model_state.fields)

migration = Migration("fake_migration", model_state.app_label)
migration.operations.append(cm)

# SHOULD ProjectState be used for the new definition to be APPLIED to DB and HOW?
state = ProjectState()
with db_conn.schema_editor(collect_sql=True, atomic=migration.atomic) as schema_editor:
     # Following create_model also doesnot migrate to mysql DB
     # Gives a Table exists Error even with root user of mysql
     # schema_editor.create_model(model_definition)

     # Following doesnot migrate to the new required state
     state = migration.apply(state, schema_editor, collect_sql=True)
     # Following gives atomic transaction error if used along with atomic
     # following commit commented gives no error but doesnt migrate
     # db_conn.commit()

我已阅读此内容并使用How to programmatically generate the CREATE TABLE SQL statement for a given model in Django?

欢迎任何帮助或资源。

更新:我确实尝试了 Django 的测试用例,但它不能以编程方式工作。我必须明确使用 addfield 吗?不确定如何让这个工作。 projectstate 和 model_create 方式都不起作用

【问题讨论】:

你有一个我可以查看的 git repo,其中包含一个 reproducible example?从初始迁移开始时,我提供的代码有效,因此我认为问题出在您的迁移图上。 【参考方案1】:

首先,您需要使用模型元类,即。 ModelBase,而不是 type

from django.db.models.base import ModelBase

model_definition = ModelBase(
    model_item.table_name,
    bases,
    model_config
)

一旦您使用了正确的元类,您可能会收到无数错误,因为您使用了许多 ModelBase 内部设置的类属性,而不是期待你自己设置。

您应该只设置 ModelBase 期望在传统模型上设置的属性,而不是转储模型具有的所有属性,其中包括:

__module____qualname__ 模型字段 自定义管理器或查询集 模型方法 型号Meta

其他的都应该省略。

例如,如果您有一个看起来像这样的模型,在模块 myapp.models 中:

class Parent(models.Model):
    name = models.CharField(max_length=45)

class Child(models.Model):
    name = models.CharField(max_length=45)
    parent = models.ForeignKey(Parent, on_delete=models.CASCADE)

class ModelWithMeta(models.Model):
    class Meta:
        db_table = 'some_table'

这些模型的动态版本需要如下所示:

from django.db import models
from django.db.models.base import ModelBase

bases = (models.Model,)

Parent = ModelBase('Parent', bases, 
    '__module__': 'myapp.models',
    '__qualname__': 'Parent',
    'name': models.CharField(max_length=45),
)

Child = ModelBase('Child', bases, 
    '__module__': 'myapp.models',
    '__qualname__': 'Child',
    'name': models.CharField(max_length=45),
    'parent': models.ForeignKey('myapp.Parent', on_delete=models.CASCADE),
)

ModelWithMeta = ModelBase('ModelWithMeta', bases, 
    '__module__': 'myapp.models',
    '__qualname__': 'ModelWithMeta',
    'Meta': type('Meta', (), 'db_table': 'some_table'),
)

我不明白你的迁移代码的目的,所以我会假设这是一个试图让动态模型工作的黑客,这意味着你可以完全扔掉它并使用内置的迁移加载器,即:

python3 manage.py makemigrations myapp && python3 manage.py migrate myapp

如果您不熟悉 python metaclasses,我建议您阅读它们,因为这是理解我的代码的先决条件。

【讨论】:

很好的解释。我真的很感谢你花时间做这件事。我将尝试使用 ModelState 进行迁移。我只有这个结构,我没有 qualname,而是我有 app_label 和 module_name。让我尝试用另一种方式解释这一点:我可以使用 app_label 方式(不带 qualname)来创建模型。但是,当我尝试使用 create_model 或 CreateModel 类迁移到带有已创建表的数据库时,我无法让它应用迁移。 >> 什么,我在使用 CreateModel 类或 create_model 函数和迁移时做错了,它没有迁移到 mysql db? 现在,正在使用当前代码正确获取 ModelState。即使 ProjectState 具有正确的迁移新状态字段,它也不仅仅是使用 CreateModel 类或 create_model 迁移 API 迁移到带有新 ProjectState 的 DB。我还将尝试使用 ModelBase 使用相同的代码,但现在它没有迁移到 DB,这就是问题 坦率地说,答案并不能解决实际问题。 ProjectState 和 ModelState 上有什么我做错了,或者我对 create_model 函数做错了吗?我必须明确使用 addfield 和 removefield 吗?我之前能够使用 create_model 进行迁移。它突然停止工作,我现在已经 2 周挠头了 我刚刚在重新加载以显示管理员注册正确时遇到问题。我安装了 uvicorn 和 gunicorn,它在安装 gunicorn 服务器并使用它运行代码后停止工作。现在,即使使用默认的开发服务器,它也不起作用。是 Django 代码库问题吗?似乎没有。我尝试了测试用例的用法,但没有使用我的代码。看起来像我的代码。但仍在检查。在此之前,我可以使用此代码进行迁移 ***.com/questions/64222558/… @Gary 您能否更新一下您的问题,究竟您是如何尝试实施我的答案的?我提供的答案“适用于我的机器”,所以我真的没有 MRE 可以使用。

以上是关于无法使用 Django 3.0.3 中的迁移 API 使用 ModelState 和 ProjectState 进行迁移的主要内容,如果未能解决你的问题,请参考以下文章

Django迁移RunPython无法调用模型方法

Django - 无法为具有动态 upload_to 值的 ImageField 创建迁移

Django迁移无法将用户实例分配给用户字段

在 Ubuntu 20.4 上使用 postgresql 设置的 Django cookiecutter 无法迁移

Django 迁移失败 - 无法查询“peterson”:必须是“用户”实例

Django 从 MySQL 迁移到 Postgres