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

时间:2011-01-19 09:30:37

标签: python django django-models

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

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

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

14 个答案:

答案 0 :(得分:376)

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

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

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

有关UPDATE / INSERT here的更多信息。

答案 1 :(得分:122)

数据库查询的Django文档包括a section on copying model instances。假设您的主键是自动生成的,您将获得要复制的对象,将主键设置为None,然后再次保存对象:

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

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

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

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


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

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

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

答案 2 :(得分:40)

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

from copy import deepcopy

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

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

答案 3 :(得分:22)

使用以下代码:

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)

答案 4 :(得分:20)

有一个克隆代码段here,您可以将其添加到模型中,这样做:

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)

答案 5 :(得分:19)

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

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

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

答案 6 :(得分:4)

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

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

答案 7 :(得分:3)

我遇到了接受答案的几个陷阱。这是我的解决方案。

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

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

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

copy.copy似乎以期望的方式生成Django模型实例的浅表副本。这是我没有找到记录的事情之一,但它可以通过酸洗和去除斑点,所以它可能得到很好的支持。

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

删除_prefetched_objects_cache是一种快速而肮脏的方法,可以删除所有预取。后续的多次访问就好像从未进行过预取一样。使用以下划线开头的未记录的属性可能会要求兼容性问题,但它现在可以使用。

答案 8 :(得分:1)

如果您有OneToOneField,则应采用以下方式:

    tmp = Foo.objects.get(pk=1)
    tmp.pk = None
    tmp.id = None
    instance = tmp

答案 9 :(得分:0)

克隆具有多个继承级别的模型,即&gt; = 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)

请参阅问题here

答案 10 :(得分:0)

试试这个

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

答案 11 :(得分:0)

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

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

答案 12 :(得分:0)

这会生成一个内存中的副本,您可以独立对其进行变异。

original = CheckoutItem(title="test", ...)
copy = CheckoutItem()

for f in CheckoutItem._meta.fields:
   setattr(copy, f.attname, getattr(original, f.attname))

或者,作为一种方法:


    def clone(self):
        """Returns a clone of this instance."""

        clone = self.__class__()
        for f in self.__class__._meta.fields:
            setattr(clone, f.attname, getattr(self, f.attname))

        return clone

答案 13 :(得分:0)

有一个可以执行此操作的软件包,可以在django管理站点中创建UI:https://github.com/RealGeeks/django-modelclone

pip install django-modelclone

将“ modelclone”添加到INSTALLED_APPS并将其导入admin.py中。

然后,每当您要使模型成为可克隆模型时,只需在给定的管理模型类“ modelclone.ClonableModelAdmin”中替换“ admin.ModelAdmin”。这将导致“重复”按钮出现在该给定模型的实例详细信息页面中。