Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在Django中批量创建自动字段_Python_Django - Fatal编程技术网

Python 在Django中批量创建自动字段

Python 在Django中批量创建自动字段,python,django,Python,Django,我有两个模型,我想将所有数据从一个迁移到另一个。为了模拟问题,假设以下模型: from django.db import models class Book: name = models.CharField(max_length=100) is_archived = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True) class BookArc

我有两个模型,我想将所有数据从一个迁移到另一个。为了模拟问题,假设以下模型:

from django.db import models

class Book:
    name = models.CharField(max_length=100)
    is_archived = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)


class BookArchived:
    name = models.CharField(max_length=100)
    created_at = models.DateTimeField(auto_now_add=True)
迁移的代码是:

book_objs = []
for archived_book in BookArchived.objects.all():
    book_objs.append(Book(name=archived_book.name, is_archived=True, created_at=archived_book.created_at))

Book.objects.bulk_create(book_objs)
这段代码的问题是,尽管我显式地设置了
created_at
字段(
created_at=archived_book.created_at
),但Django会在当前时间插入所有这些字段

我知道如果显式设置了
pk
字段(
id=archived\u book.id
),新对象将保留原始时间戳。但数据库中已经存在重叠的ID,所以这种方法将产生另一个问题。如何保存在
BookArchived
实例中创建的
原始值?

我找到了这个应该可以工作的gem,但我自己还没有测试过


从contextlib导入contextmanager
@上下文管理器
def suppress_auto_now(型号、字段名称):
"""
从…起https://stackoverflow.com/a/59898220/519995
这是我的主意https://stackoverflow.com/a/35943149/1731460
"""
字段_state={}
对于字段名称中的字段名称:
field=model.\u meta.get\u字段(字段名称)
fields_state[field]={'auto_now':field.auto_now,'auto_now_add':field.auto_now_add}
对于处于字段状态的字段:
field.auto_now=False
field.auto\u now\u add=False
尝试:
产量
最后:
对于字段,字段中的状态\u state.items():
field.auto\u now=状态['auto\u now']
field.auto\u now\u add=状态['auto\u now\u add']
像这样使用它:

with suppress_autotime(Book, ['created_at']):
        Book.objects.bulk_create(book_objs)
注意: 请勿在视图/表单中或Django应用程序中的任何位置使用此上下文管理器。此上下文管理器更改字段的内部状态(通过临时设置
auto\u now
auto\u now\u add
False
)。这将导致Django在执行并发请求的上下文管理器主体(即同一进程、不同线程)期间,不使用时区.now()填充这些字段。尽管这可用于独立脚本(例如管理命令、数据迁移),但这些脚本与Django应用程序不在同一进程中运行。

日期字段中的注释所示:

按照当前的实现,将auto_now或auto_now_add设置为True 将导致字段设置为可编辑=False和空白=True

由于
editable
将设置为
False
,因此向模型提供任何值都将无效。您需要在多次迁移中执行此步骤

首先让没有自动的模型现在添加:

class Book(models.Model):
    name = models.CharField(max_length=100)
    is_archived = models.BooleanField(default=False)
    created_at = models.DateTimeField()
使用
python manage.py makemigrations
为此生成迁移。接下来我们要做一个测试。首先运行
python manage.py makemigrations--empty
这将创建一个空的迁移,您将编辑该迁移以将数据从
BookArchived
复制到
Book
。这看起来像:

from django.db import migrations

def copy_legacy_books(apps, schema_editor):
    # We can't import the Person model directly as it may be a newer
    # version than this migration expects. We use the historical version.
    Book = apps.get_model('yourappname', 'Book')
    BookArchived = apps.get_model('yourappname', 'BookArchived')
    book_objs = []
    for archived_book in BookArchived.objects.all():
        book_objs.append(Book(name=archived_book.name, is_archived=True, created_at=archived_book.created_at))
    
    Book.objects.bulk_create(book_objs)

class Migration(migrations.Migration):

    dependencies = [
        ('yourappname', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(copy_legacy_books),
    ]
现在,在此之后,只需将
auto\u Now\u add
kwarg添加到日期时间字段,并使用
python manage.py makemigrations
生成另一个迁移:

class Book:
    name = models.CharField(max_length=100)
    is_archived = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)

@布隆德格:就是这样。我不明白你为什么需要一个额外的型号。您有一个布尔值来确定
书籍是否已存档
,因此您可以轻松提取已存档的书籍。@WillemVanOnsem它是旧版模型。现在数据存在于两个不同的表中。这就是为什么我要将所有数据从
BookArchived
迁移到
Book
,然后删除前一个模型。@ElginCahangirov您需要从
Book
模型中删除
auto\u now\u add=True
,然后在另一个迁移中重新添加它
auto\u now\u add
隐式设置
editable=False
,因此您不能自己设置它的值。@AbdulAzizBarkat不适用于生产环境。在部署时,将有对book表的插入(
book
model),在这种情况下,此解决方案按预期工作。但我认为,在视图/表单中或项目中的任何地方使用这种方法都是危险的,因为上下文管理器正在改变模型字段的内部状态。此更改将对运行django应用程序的整个进程产生影响,因此在从上下文管理器输入和退出时间之间进行保存时,django不会自动填充字段。你觉得怎么样?我在django视图中测试了这段代码。正如我所设想的,它会导致尝试创建新的
Book
实例的并发请求出错,因为
suppress\u auto\u now
上下文管理器会在
字段更改
created\u的状态。因此,在django应用程序中使用此代码并不安全,尽管它符合我的需要。我将在django管理命令中运行迁移代码,该命令将在django应用程序的单独进程中执行。请接受我的编辑,以便将您的答案标记为已接受。您是对的@ElginCahangirov!如果在Web服务器内部使用,这是非常危险的,我只考虑在服务器进程之外运行的数据迁移脚本。-编辑接受,谢谢!谢谢你的努力。提出的解决方案克服了我的问题。但请考虑对应用程序的高并发请求。当我的自定义迁移执行
Book时,在
处创建的\u将不会是
auto\u now\u add
,这将导致服务器错误。@ElginCahangirov这将如何导致错误?在应用所有迁移之前,服务器甚至不应该运行。考虑到运行了
python manage.py migrate
,所有这3个迁移都将按顺序应用,之后,如果需要或使用fixture,您可以在另一个数据迁移中添加一些book实例(根据您的注释“部署时将插入book表”)。完成所有迁移并加载初始数据后,理想情况下,服务器应使用
python manage.py runserver运行
@ElginCahangirov查看如何为模型提供初始数据。假设数据迁移需要2分钟(或