【发布时间】:2023-03-28 07:49:01
【问题描述】:
Foo.objects.get(pk="foo")
<Foo: test>
在数据库中,我想添加另一个对象,它是上面对象的副本。
假设我的桌子只有一行。我想将第一行对象插入具有不同主键的另一行。我该怎么做?
【问题讨论】:
标签: python django django-models
Foo.objects.get(pk="foo")
<Foo: test>
在数据库中,我想添加另一个对象,它是上面对象的副本。
假设我的桌子只有一行。我想将第一行对象插入具有不同主键的另一行。我该怎么做?
【问题讨论】:
标签: python django django-models
只需更改对象的主键并运行 save()。
obj = Foo.objects.get(pk=<some_existing_pk>)
obj.pk = None
obj.save()
如果您想要自动生成的密钥,请将新密钥设置为无。
更多关于更新/插入here。
关于复制模型实例的官方文档:https://docs.djangoproject.com/en/2.2/topics/db/queries/#copying-model-instances
【讨论】:
obj.pk 和 obj.id 才能在 Django 1.4 中进行这项工作
datetime 字段,它将改变
数据库查询的 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
在这个 sn-p 中,第一个 save() 创建原始对象,第二个 save() 创建副本。
如果您继续阅读文档,还有一些关于如何处理两个更复杂情况的示例:(1) 复制作为模型子类实例的对象,以及 (2) 还复制相关对象,包括多对多关系。
关于 miah 的回答的注意事项:miah 的回答中提到了将 pk 设置为 None,尽管它没有显示在前面和中间。所以我的回答主要是为了强调这种方法是 Django 推荐的方法。
历史记录:直到 1.4 版的 Django 文档中才对此进行了解释。不过,从 1.4 之前就已经有可能了。
未来可能的功能:上述文档更改是在this ticket 中进行的。在票的评论线程上,还有一些关于为模型类添加内置 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)
【讨论】:
model_to_dict 采用exclude 参数,这意味着您不需要单独的pop: model_to_dict(instance, exclude=['id'])
有一个克隆 sn-p 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)
【讨论】:
if 现在需要为if fld.name != old._meta.pk.name,即_meta.pk 实例的name 属性。
如何做到这一点已添加到 Django1.4 中的官方 Django 文档中
https://docs.djangoproject.com/en/1.10/topics/db/queries/#copying-model-instances
官方回答与 miah 的回答类似,但文档指出了继承和相关对象的一些困难,因此您可能应该确保阅读文档。
【讨论】:
stable 而不是 URL 中的版本号,如下所示:docs.djangoproject.com/en/stable/topics/db/queries/…
我在接受的答案中遇到了几个问题。这是我的解决方案。
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 是一种删除所有预取的快速而肮脏的方法。随后的多次访问就像从来没有预取一样工作。使用以下划线开头的未记录属性可能会导致兼容性问题,但它现在可以使用。
【讨论】:
_[model_name]_cache 的属性,一旦删除,我就可以分配一个该相关模型的新 ID,然后调用 save()。仍然可能有我尚未确定的副作用。
设置 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
【讨论】:
pk 和id,保存克隆将有效地更新克隆对象。
clone.pk = None。 (如果主键是其他字段,我建议使用pk 而不是id,例如:uuid)。
克隆具有多个继承级别的模型,即 >= 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。
【讨论】:
试试这个
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”即可。这会导致该给定模型的实例详细信息页面中出现一个“复制”按钮。
【讨论】:
如果你有OneToOneField,那么你应该这样做:
tmp = Foo.objects.get(pk=1)
tmp.pk = None
tmp.id = None
instance = tmp
【讨论】:
这个简单的过程对我来说很好用:
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()
【讨论】: