使用 Django 中的新用户字段获取并保存 QuerySet 的副本

Fetch and save a copy of a QuerySet with a new User field in Django

我有很多模型,每个模型都有一个用户字段。每次注册新用户时,我都想复制这些模型的实例 user.id = 1,并在用户字段中使用新用户 ID 保存它们。我读过可以通过将 pk 设置为 None 来制作模型实例的副本,所以这是我首先尝试做的。我正在尝试在 QuerySet 上执行此操作,因为某些 table 有很多实例(某些型号超过 1000,但大部分少于 100)。

def copy_tables(user):
    # This approach fails with "Voice has no field named 'pk'"
    voice = Voice.objects.filter(user=1).update(
        pk=None, user=user.id, right_answers=0, wrong_answers=0)

我也尝试过使用 copy.deepcopy(Voice.objects.filter(user=1)),但调用 update() 仍然会更改数据库中的原始 table。

def copy_tables(user):
    copy.deepcopy(Voice.objects.filter(user=1)).update(
        user=user.id, right_answers=0, wrong_answers=0)
    # This test throws an error because the original models have been changed
    assert Voice.objects.filter(user=1).first()

我是不是错过了其他的方法?此外,我认为所有模型实例都有一个主键 pk,它由 Django 自动设置。不是这样吗?

当在模型实例上调用save方法时,Django 通过检查它是否具有主键(ref.) 来确定是否必须插入或更新它。

因此,如果您在模型实例上将 PK 设置为 None,Django 将尝试将其作为新行添加到数据库中。

演示:

# Retrieve the object belonging to the user whose ID is 1.
# Note that you cannot use "user=1", you need to tell Django you
# are querying by id, using "user__id=1" .
obj = MyModel.objects.get(user__id=1) 

# Set PK to None so that Django thinks it is not save in the database.
obj.pk = None

# Save to insert it.
obj.save()

如果你有多个对象,你可以使用 bulk_create 来提高性能:

voices = Voice.objects.filter(user__id=1)
new_voices = []
for voice in voices:
    voice.pk = None
    voice.user = user
    right_answsers = 0
    wrong_anwsers = 0
    new_voices.append(voice)

Voice.objects.bulk_create(new_voices)