Foo.objects.get(pk="foo")
<Foo: test>
ในฐานข้อมูลฉันต้องการเพิ่มวัตถุอื่นซึ่งเป็นสำเนาของวัตถุด้านบน
สมมติว่าตารางของฉันมีหนึ่งแถว ฉันต้องการแทรกวัตถุแถวแรกในแถวอื่นด้วยคีย์หลักที่แตกต่างกัน ฉันจะทำสิ่งนั้นได้อย่างไร
Foo.objects.get(pk="foo")
<Foo: test>
ในฐานข้อมูลฉันต้องการเพิ่มวัตถุอื่นซึ่งเป็นสำเนาของวัตถุด้านบน
สมมติว่าตารางของฉันมีหนึ่งแถว ฉันต้องการแทรกวัตถุแถวแรกในแถวอื่นด้วยคีย์หลักที่แตกต่างกัน ฉันจะทำสิ่งนั้นได้อย่างไร
คำตอบ:
เพียงเปลี่ยนคีย์หลักของวัตถุของคุณแล้วเรียกใช้ save ()
obj = Foo.objects.get(pk=<some_existing_pk>)
obj.pk = None
obj.save()
หากคุณต้องการคีย์ที่สร้างขึ้นอัตโนมัติตั้งค่าคีย์ใหม่เป็นไม่มี
เพิ่มเติมเกี่ยวกับการปรับปรุง / INSERT ที่นี่
เอกสารอย่างเป็นทางการเกี่ยวกับการคัดลอกตัวอย่างแบบจำลอง: https://docs.djangoproject.com/en/2.2/topics/db/queries/#copying-model-instances
obj.pk
และobj.id
เพื่อให้งานนี้ใน Django 1.4
เอกสาร 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: การตั้งค่า pk จะNone
ถูกกล่าวถึงในคำตอบของ miah แม้ว่าจะไม่ได้นำเสนอด้านหน้าและกึ่งกลาง ดังนั้นคำตอบของฉันส่วนใหญ่จะเน้นย้ำวิธีการนั้นเป็นวิธีที่ Django แนะนำให้ทำ
ประวัติบันทึกย่อ: สิ่งนี้ไม่ได้อธิบายไว้ในเอกสาร Django จนถึงรุ่น 1.4 มันเป็นไปได้ตั้งแต่ก่อน 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)
model_to_dict
รับexclude
พารามิเตอร์ซึ่งหมายความว่าคุณไม่จำเป็นต้องแยกจากpop
กัน:model_to_dict(instance, exclude=['id'])
มีตัวอย่างโค้ดที่นี่ซึ่งคุณสามารถเพิ่มลงในโมเดลของคุณซึ่งทำสิ่งนี้:
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
เช่นname
คุณสมบัติของ_meta.pk
อินสแตนซ์
วิธีการทำเช่นนี้ถูกบันทึกอยู่ในเอกสารทางการของ Django ใน Django1.4
https://docs.djangoproject.com/en/1.10/topics/db/queries/#copying-model-instances
คำตอบอย่างเป็นทางการคล้ายกับคำตอบของ miah แต่เอกสารชี้ให้เห็นปัญหาบางอย่างเกี่ยวกับการสืบทอดและวัตถุที่เกี่ยวข้องดังนั้นคุณควรแน่ใจว่าคุณอ่านเอกสาร
stable
แทนหมายเลขรุ่นใน URL เช่นนี้docs.djangoproject.com/en/stable/topics/db/queries/...
ฉันเจอคู่ gotchas กับคำตอบที่ยอมรับได้ นี่คือทางออกของฉัน
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
เป็นวิธีที่รวดเร็วและสกปรกในการแยกไฟล์ prefetch ทั้งหมดออก การเข้าถึงหลายครั้งที่ตามมาจะทำงานราวกับว่าไม่มีการดึงข้อมูลล่วงหน้า การใช้คุณสมบัติที่ไม่มีเอกสารที่ขึ้นต้นด้วยเครื่องหมายขีดล่างอาจเป็นการถามถึงปัญหาความเข้ากันได้ แต่มันได้ผลในตอนนี้
_[model_name]_cache
ซึ่งเมื่อลบแล้วฉันก็สามารถที่จะกำหนด ID save()
ใหม่สำหรับรูปแบบที่เกี่ยวข้องที่โทรแล้ว อาจมีผลข้างเคียงที่ฉันยังไม่ได้ตัดสินใจ
การตั้งค่า pk เป็นไม่มีดีกว่า sinse 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)
หากต้องการโคลนโมเดลที่มีหลายระดับการสืบทอดเช่น> = 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()