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

use*_*795 242 python django django-models

Foo.objects.get(pk="foo")
<Foo: test>
Run Code Online (Sandbox Code Playgroud)

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

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

mia*_*iah 406

只需更改对象的主键并运行save()即可.

obj = Foo.objects.get(pk=<some_existing_pk>)
obj.pk = None
obj.save()
Run Code Online (Sandbox Code Playgroud)

如果需要自动生成密钥,请将新密钥设置为"无".

更多关于UPDATE/INSERT的信息.

  • 注意:如果有外键,涉及one2one和m2m(即,可能存在更复杂的"深层复制"方案),事情可能会更复杂一些 (9认同)
  • 在1.4.1中正常工作这可能是其中一项将持续工作很长时间的事情之一. (7认同)
  • 我必须设置`obj.pk`和`obj.id`才能在Django 1.4中使用它 (7认同)
  • 值得注意的是,这引用了Django 1.2,我们现在可以使用Django 1.4了.没有测试这是否有效,但不要使用这个答案而不确定它是否适合你. (2认同)
  • @PetrPeller - [docs](https://docs.djangoproject.com/en/1.6/topics/db/queries/#copying-model-instances)表明这是因为你正在使用模型继承. (2认同)
  • 如果有一个`datetime`字段,它将会改变 (2认同)

S. *_*rby 130

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

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

blog.pk = None
blog.save() # blog.pk == 2
Run Code Online (Sandbox Code Playgroud)

在此代码段中,第一个save()创建原始对象,第二个save()创建副本.

如果你继续阅读文档,还有一些例子来说明如何处理两个更复杂的情况:(1)复制一个对象,它是一个模型子类的实例,(2)还复制相关的对象,包括多个对象 - 很多关系.


关于miah答案的注意事项:在miah的答案None中提到了设置pk ,虽然它没有显示在前面和中间.所以我的回答主要是强调该方法是Django推荐的方法.

历史记录:在版本1.4之前,Django文档中没有解释这一点.但是,自1.4之前就有可能.

可能的未来功能:上述文档更改在此票证中进行.在故障单的评论帖子中,还有一些关于copy为模型类添加内置函数的讨论,但据我所知他们决定不解决这个问题.因此,这种"手动"复制方式可能现在必须要做.


Tro*_*eld 42

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

from copy import deepcopy

new_instance = deepcopy(object_you_want_copied)
new_instance.id = None
new_instance.save()
Run Code Online (Sandbox Code Playgroud)

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

  • 并且也不适用于"通过"关系 (4认同)
  • 如果模型具有多个继承级别,则此方法不起作用。 (2认同)

t_i*_*_io 26

使用以下代码:

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)
Run Code Online (Sandbox Code Playgroud)

  • `model_to_dict`采用`exclude`参数,这意味着你不需要单独的`pop`:`model_to_dict(instance,exclude = ['id'])` (6认同)
  • 这将导致外键异常 (2认同)

Dom*_*ger 20

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

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)
Run Code Online (Sandbox Code Playgroud)


Mic*_*tra 20

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

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

官方答案类似于miah的答案,但是文档指出了继承和相关对象的一些困难,所以你应该确保你阅读文档.


mor*_*tar 6

我遇到了一些公认的答案。这是我的解决方案。

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
Run Code Online (Sandbox Code Playgroud)

注意:这使用的解决方案未在Django文档中得到正式批准,因此在以后的版本中可能会停止使用。我在1.9.13中进行了测试。

第一个改进是,它允许您通过使用继续使用原始实例copy.copy。即使您不打算重用该实例,如果要克隆的实例作为参数传递给函数,执行此步骤也可能更安全。否则,函数返回时,调用方将意外地拥有其他实例。

copy.copy似乎以所需的方式生成了Django模型实例的浅表副本。这是我未发现的东西之一,但是它可以通过酸洗和酸洗来工作,因此可能得到了很好的支持。

其次,批准的答案将把任何预取结果附加到新实例上。除非您显式复制多对多关系,否则这些结果不应与新实例相关联。如果遍历预取的关系,将得到与数据库不匹配的结果。添加预取时破坏工作代码可能会令人讨厌。

删除_prefetched_objects_cache是一种剥离所有预取的快捷方法。随后的许多访问就像从未进行过预取一样工作。使用以下划线开头的未记录的属性可能会引起兼容性问题,但现在可以使用。


Ard*_*ine 5

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

object_copy = MyObject.objects.get(pk=...)
object_copy.pk = None
object_copy.save()
Run Code Online (Sandbox Code Playgroud)


Aht*_*ham 5

这是克隆模型实例的另一种方法:

d = Foo.objects.filter(pk=1).values().first()   
d.update({'id': None})
duplicate = Foo.objects.create(**d)
Run Code Online (Sandbox Code Playgroud)