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

2025-03-06 08:54:00
admin
原创
35
摘要:问题描述:Foo.objects.get(pk="foo") <Foo: test> 在数据库中,我想添加另一个对象,它是上述对象的副本。假设我的表有一行。我想将第一行对象插入到具有不同主键的另一行中。我该怎么做?解决方案 1:只需改变对象的主键并运行save()。obj = ...

问题描述:

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

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

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


解决方案 1:

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

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

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

有关 UPDATE/INSERT 的更多信息请见此处。

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

解决方案 2:

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函数的讨论,但据我所知,他们决定暂时不解决这个问题。因此,这种“手动”复制方式可能暂时只能这样了。

解决方案 3:

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

from copy import deepcopy

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

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

解决方案 4:

使用以下代码:

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)

解决方案 5:

这里有一个克隆片段,您可以将其添加到执行此操作的模型中:

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)

解决方案 6:

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

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

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

解决方案 7:

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

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 模型实例的浅表副本。这是我没有找到文档的内容之一,但它通过 pickling 和 unpickling 工作,因此它可能得到了很好的支持。

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

删除_prefetched_objects_cache是去除所有预取的快捷方式。后续的对多访问就像从未进行过预取一样。使用以下划线开头的未记录属性可能会带来兼容性问题,但目前它是可行的。

解决方案 8:

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

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

解决方案 9:

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

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

解决方案 10:

这会在内存中执行复制,您可以独立对其进行修改。

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

解决方案 11:

Django 5.1中,您应该设置obj.pk=Noneobj._state.adding = True,然后在docs.djangoproject.com/en/5.1/topics/db/queries/#copying-model-instancesobj.save()中查看更多信息。

如果您使用模型继承,您也应该设置obj.id = None

完整示例:

obj = SomeModel.objects.get(pk=1)
obj.pk = None 
obj.id = None
obj._state.adding = True
obj.save()  # now obj.pk = 2

解决方案 12:

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

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()

解决方案 13:

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

请参考此处的问题。

解决方案 14:

尝试一下

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

解决方案 15:

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

pip install django-modelclone

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

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

解决方案 16:

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

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

解决方案 17:

这似乎是最直接和最可靠的 IAW Django 5.1 标准。这里添加的是内部查询以获取对正在复制的对象的新引用。如果您未能做到这一点,则在调用后,copy()您可能会对同一对象有 2 个引用。

模型类定义内部的这个看起来简单而有效:

def copy(self) -> Self:
    dupe = <ModelName>.objects.get(pk=self.pk)
    dupe.pk = None
    dupe._state.adding = True
    dupe.save()
    return dupe
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1887  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1425  
  在制造业数字化转型的进程中,PLM(产品生命周期管理)系统、ERP(企业资源计划)系统、MES(制造执行系统)以及 CAD(计算机辅助设计)软件都扮演着至关重要的角色。然而,这些系统和软件各自独立运行时,往往难以发挥出最大的协同效应。实现 PLM 系统与 ERP、MES、CAD 的有效集成,成为提升企业整体竞争力、优化...
plm系统的主要功能模块   3  
  产品生命周期管理(PLM)作为一种先进的管理理念和技术,在电子与半导体行业正发挥着日益重要的作用。随着电子与半导体行业的快速发展,产品更新换代速度加快,市场竞争愈发激烈,企业面临着诸多挑战,如缩短产品上市时间、提高产品质量、降低成本等。而PLM的应用为企业应对这些挑战提供了有效的解决方案,展现出巨大的应用价值。提升产品...
plm项目   4  
  PLM(产品生命周期管理)项目管理软件在现代企业的产品研发、生产与运营中扮演着至关重要的角色。它整合了从产品概念设计到退役的全流程数据与流程,助力企业提升效率、降低成本并增强创新能力。随着科技的飞速发展以及企业需求的不断演变,未来十年 PLM 项目管理软件的发展充满了无限可能,值得深入探讨与预测。智能化与自动化趋势智能...
plm产品全生命周期管理   6  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用