Python 如何克隆 Django 模型实例对象并将其保存到数据库?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4733609/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 17:15:02  来源:igfitidea点击:

How do I clone a Django model instance object and save it to the database?

pythondjangodjango-models

提问by user426795

Foo.objects.get(pk="foo")
<Foo: test>

In the database, I want to add another object which is a copy of the object above.

在数据库中,我想添加另一个对象,它是上述对象的副本。

Suppose my table has one row. I want to insert the first row object into another row with a different primary key. How can I do that?

假设我的表只有一行。我想将第一行对象插入到具有不同主键的另一行中。我怎样才能做到这一点?

采纳答案by miah

Just change the primary key of your object and run save().

只需更改对象的主键并运行 save()。

obj = Foo.objects.get(pk=<some_existing_pk>)
obj.pk = None
obj.save()

If you want auto-generated key, set the new key to None.

如果您想要自动生成的密钥,请将新密钥设置为无。

More on UPDATE/INSERT here.

更多关于更新/插入在这里

Official docs on copying model instances: https://docs.djangoproject.com/en/2.2/topics/db/queries/#copying-model-instances

关于复制模型实例的官方文档:https: //docs.djangoproject.com/en/2.2/topics/db/queries/#copying-model-instances

回答by Dominic Rodger

There's a clone snippet here, which you can add to your model which does this:

有一个克隆片段在这里,你可以添加到您的模型,做到这一点:

def clone(self):
  new_kwargs = dict([(fld.name, getattr(old, fld.name)) for fld in old._meta.fields if fld.name != old._meta.pk]);
  return self.__class__.objects.create(**new_kwargs)

回答by S. Kirby

The Django documentation for database queries includes a section on copying model instances. Assuming your primary keys are autogenerated, you get the object you want to copy, set the primary key to None, and save the object again:

用于数据库查询的 Django 文档包括一个关于复制模型实例的部分。假设您的主键是自动生成的,您将获得要复制的对象,将主键设置为None,然后再次保存该对象:

blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1

blog.pk = None
blog.save() # blog.pk == 2

In this snippet, the first save()creates the original object, and the second save()creates the copy.

在这个片段中,第一个save()创建原始对象,第二个save()创建副本。

If you keep reading the documentation, there are also examples on how to handle two more complex cases: (1) copying an object which is an instance of a model subclass, and (2) also copying related objects, including objects in many-to-many relations.

如果您继续阅读文档,还有关于如何处理两种更复杂情况的示例:(1) 复制作为模型子类实例的对象,以及 (2) 还复制相关对象,包括多对中的对象-许多关系。



Note on miah's answer: Setting the pk to Noneis mentioned in miah's answer, although it's not presented front and center. So my answer mainly serves to emphasize that method as the Django-recommended way to do it.

关于 miah 的回答的注意事项:miah 的回答None中提到了将 pk 设置为,尽管它没有出现在前面和中间。所以我的回答主要是为了强调该方法是 Django 推荐的方法。

Historical note: This wasn't explained in the Django docs until version 1.4. It has been possible since before 1.4, though.

历史记录:直到 1.4 版,Django 文档中才解释了这一点。不过,从 1.4 之前就可以了。

Possible future functionality: The aforementioned docs change was made in this ticket. On the ticket's comment thread, there was also some discussion on adding a built-in copyfunction for model classes, but as far as I know they decided not to tackle that problem yet. So this "manual" way of copying will probably have to do for now.

未来可能的功能:上述文档更改已在此票证中进行。在票据的评论线程上,也有一些关于copy为模型类添加内置函数的讨论,但据我所知,他们决定不解决这个问题。所以这种“手动”复制方式现在可能不得不做。

回答by Michael Bylstra

How to do this was added to the official Django docs in Django1.4

如何做到这一点已添加到 Django1.4 的官方 Django 文档中

https://docs.djangoproject.com/en/1.10/topics/db/queries/#copying-model-instances

https://docs.djangoproject.com/en/1.10/topics/db/queries/#copying-model-instances

The official answer is similar to miah's answer, but the docs point out some difficulties with inheritance and related objects, so you should probably make sure you read the docs.

官方答案类似于 miah 的答案,但文档指出继承和相关对象存在一些困难,因此您可能应该确保阅读文档。

回答by Troy Grosfield

Be careful here. This can be extremely expensive if you're in a loop of some kind and you're retrieving objects one by one. If you don't want the call to the database, just do:

这里要小心。如果您处于某种循环中并且您正在一个一个地检索对象,这可能会非常昂贵。如果您不想调用数据库,只需执行以下操作:

from copy import deepcopy

new_instance = deepcopy(object_you_want_copied)
new_instance.id = None
new_instance.save()

It does the same thing as some of these other answers, but it doesn't make the database call to retrieve an object. This is also useful if you want to make a copy of an object that doesn't exist yet in the database.

它与其他一些答案做同样的事情,但它不会调用数据库来检索对象。如果要复制数据库中尚不存在的对象,这也很有用。

回答by t_io

Use the below code :

使用以下代码:

from django.forms import model_to_dict

instance = Some.objects.get(slug='something')

kwargs = model_to_dict(instance, exclude=['id'])
new_instance = Some.objects.create(**kwargs)

回答by Ardine

setting pk to None is better, sinse Django can correctly create a pk for you

将 pk 设置为 None 更好,因为 Django 可以为您正确创建一个 pk

object_copy = MyObject.objects.get(pk=...)
object_copy.pk = None
object_copy.save()

回答by David Cheung

To clone a model with multiple inheritance levels, i.e. >= 2, or ModelC below

克隆一个多继承级别的模型,即>=2,或者下面的ModelC

class ModelA(models.Model):
    info1 = models.CharField(max_length=64)

class ModelB(ModelA):
    info2 = models.CharField(max_length=64)

class ModelC(ModelB):
    info3 = models.CharField(max_length=64)

Please refer the question here.

请参考这里的问题。

回答by Pulkit Pahwa

Try this

尝试这个

original_object = Foo.objects.get(pk="foo")
v = vars(original_object)
v.pop("pk")
new_object = Foo(**v)
new_object.save()

回答by morningstar

I've run into a couple gotchas with the accepted answer. Here is my solution.

我在接受的答案中遇到了几个问题。这是我的解决方案。

import copy

def clone(instance):
    cloned = copy.copy(instance) # don't alter original instance
    cloned.pk = None
    try:
        delattr(cloned, '_prefetched_objects_cache')
    except AttributeError:
        pass
    return cloned

Note: this uses solutions that aren't officially sanctioned in the Django docs, and they may cease to work in future versions. I tested this in 1.9.13.

注意:这使用了 Django 文档中未正式批准的解决方案,它们可能会在未来版本中停止工作。我在 1.9.13 中对此进行了测试。

The first improvement is that it allows you to continue using the original instance, by using copy.copy. Even if you don't intend to reuse the instance, it can be safer to do this step if the instance you're cloning was passed as an argument to a function. If not, the caller will unexpectedly have a different instance when the function returns.

第一个改进是它允许您通过使用copy.copy. 即使您不打算重用该实例,如果您正在克隆的实例作为参数传递给函数,则执行此步骤会更安全。否则,当函数返回时,调用者会意外地拥有不同的实例。

copy.copyseems to produce a shallow copy of a Django model instance in the desired way. This is one of the things I did not find documented, but it works by pickling and unpickling, so it's probably well-supported.

copy.copy似乎以所需的方式生成 Django 模型实例的浅拷贝。这是我没有找到记录的事情之一,但它通过酸洗和解酸来工作,所以它可能得到了很好的支持。

Secondly, the approved answer will leave any prefetched results attached to the new instance. Those results shouldn't be associated with the new instance, unless you explicitly copy the to-many relationships. If you traverse the the prefetched relationships, you will get results that don't match the database. Breaking working code when you add a prefetch can be a nasty surprise.

其次,批准的答案将留下任何附加到新实例的预取结果。这些结果不应与新实例相关联,除非您明确复制对多关系。如果遍历预取关系,您将得到与数据库不匹配的结果。添加预取时破坏工作代码可能是一个令人讨厌的惊喜。

Deleting _prefetched_objects_cacheis a quick-and-dirty way to strip away all prefetches. Subsequent to-many accesses work as if there never was a prefetch. Using an undocumented property that begins with an underscore is probably asking for compatibility trouble, but it works for now.

删除_prefetched_objects_cache是一种去除所有预取的快速而肮脏的方法。随后的对多访问就好像从来没有预取一样工作。使用以下划线开头的未记录属性可能会导致兼容性问题,但它现在有效。