繁体   English   中英

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

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

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

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

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

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

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

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

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

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

用于数据库查询的 Django 文档包括一个关于复制模型实例的部分 假设您的主键是自动生成的,您将获得要复制的对象,将主键设置为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 之前就可以了。

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

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

from copy import deepcopy

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

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

使用以下代码:

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)

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

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

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

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

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)

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

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

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

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

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

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

这会在内存中复制您可以独立变异的副本。

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

如果你有一个OneToOneField那么你应该这样做:

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

克隆一个多继承级别的模型,即>=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)

请参考这里的问题。

试试这个

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

有一个包可以做到这一点,它在 django 管理站点中创建一个 UI: https : //github.com/RealGeeks/django-modelclone

pip install django-modelclone

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

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

这个简单的过程对我来说很好:

foo_obj = Foo.objects.get(pk="foo")
foo_values = foo_obj.__dict__
foo_values.pop('_state')
foo_values.pop('id')
foo_new_obj = Foo(**foo_values)
foo_new_obj.save()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM