Django 모델 인스턴스 객체를 어떻게 복제하여 데이터베이스에 저장합니까?


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

데이터베이스에서 위의 객체의 사본 인 다른 객체를 추가하고 싶습니다.

내 테이블에 하나의 행이 있다고 가정하십시오. 다른 기본 키를 사용하여 첫 번째 행 객체를 다른 행에 삽입하고 싶습니다. 어떻게해야합니까?

답변:


437

객체의 기본 키를 변경하고 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 1.2를 인용한다는 점을 주목할 가치가 있습니다. 우리는 이제 Django 1.4에 달려 있습니다. 이것이 효과가 있는지 테스트하지는 않았지만이 답변이 효과가 있는지 확신하지 않고는이 답변을 사용하지 마십시오.
Joe

7
1.4.1에서 잘 작동 함 이것은 아마도 오랫동안 계속 작동 할 것 중 하나 일 것입니다.
frnhr

8
Django 1.4obj.pkobj.id
Petr Peller

3
@PetrPeller- 문서 는 모델 상속을 사용하고 있기 때문에 제안합니다.
Dominic Rodger

12
참고 : 외래 키, one2one 및 m2m이 관련된 경우 상황이 좀 더 복잡 할 수 있습니다 (예 : 더 복잡한 "딥 카피"시나리오가있을 수 있음)
Ben Roberts

135

데이터베이스 쿼리에 대한 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()만들고 두 번째 는 사본을 만듭니다.

문서를 계속 읽으면 두 가지 더 복잡한 경우를 처리하는 방법에 대한 예도 있습니다. -많은 관계.


miah의 답변에 대한 참고 사항 : pk를 설정하는 None것은 miah의 답변에 언급되어 있지만 앞면과 가운데에는 나와 있지 않습니다. 그래서 제 대답은 주로 장고 권장 방법으로 그 방법을 강조하는 것입니다.

역사적 참고 사항 : 이것은 장고 문서에서 1.4 버전까지 설명되지 않았습니다. 그러나 1.4 이전부터 가능했습니다.

향후 기능 가능 : 위에서 언급 한 문서 변경 사항 이이 티켓 에서 변경되었습니다 . 티켓의 주석 스레드에서 copy모델 클래스 에 내장 함수를 추가하는 것에 대한 토론도 있었지만 아는 한 아직 그 문제를 해결하지 않기로 결정했습니다. 따라서이 "수동"복사 방법은 아마도 지금해야 할 것입니다.


46

여기서 조심하십시오. 어떤 종류의 루프에 있고 객체를 하나씩 검색하는 경우 이것은 매우 비쌀 수 있습니다. 데이터베이스를 호출하지 않으려면 다음을 수행하십시오.

from copy import deepcopy

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

이 다른 답변 중 일부와 동일한 작업을 수행하지만 객체를 검색하기 위해 데이터베이스를 호출하지는 않습니다. 데이터베이스에 아직 존재하지 않는 개체의 복사본을 만들려는 경우에도 유용합니다.


1
이것은 객체가있는 경우 효과적입니다. 변경하기 전에 원본 객체를 딥 카피하여 새 객체를 변경하고 저장할 수 있습니다. 그런 다음 조건 검사를 수행하고 통과 여부에 따라, 즉 객체가 검사중인 다른 테이블에 있으면 new_instance.id = original_instance.id를 설정하고 저장할 수 있습니다 :) 감사합니다!
radtek

2
모델에 다중 상속 레벨이있는 ​​경우 작동하지 않습니다.
David Cheung

1
내 경우에는 "self"변수를 사용하는 모델에 대한 복제 방법을 만들고 싶었고 self.pk None을 설정할 수 없으므로이 솔루션은 매력처럼 작동했습니다. 아래의 model_to_dict 솔루션에 대해 생각했지만 추가 단계가 필요하며 관통 관계와 동일한 문제가 발생합니다. 어쨌든 수동으로 처리해야하므로 큰 영향을 미치지 않습니다.
Anderson Santos

32

아래 코드를 사용하십시오 :

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)

8
model_to_dict소요 exclude당신이 별도 필요하지 않은 수단 매개 변수 pop:model_to_dict(instance, exclude=['id'])
georgebrock

20

복제 조각이있다 여기 이 수행 모델에 추가 할 수있는 :

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)

@ user426975-아, 오 잘 (내 대답에서 제거했습니다).
Dominic Rodger

이것이 장고 버전인지 확실하지 않지만 if이제는 인스턴스 if fld.name != old._meta.pk.namename속성 이어야 _meta.pk합니다.
Chris

20

이 작업을 수행하는 방법은 Django1.4의 공식 Django 문서에 추가되었습니다

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

공식적인 대답은 miah의 대답과 비슷하지만 문서는 상속 및 관련 개체에 약간의 어려움이 있음을 나타내므로 문서를 읽으십시오.


링크를 열면 페이지를 찾을 수 없습니다
Amrit

Django 1.4 용 문서가 더 이상 존재하지 않습니다. 최신 문서를 가리 키도록 답변을 업데이트하겠습니다.
Michael Bylstra

1
@MichaelBylstra 상록 링크를 사용하는 좋은 방법 stable은 URL에서 버전 번호 대신 다음과 같이 사용하는 것입니다. docs.djangoproject.com/en/stable/topics/db/queries/…
Flimm

8

나는 받아 들인 대답으로 몇 가지 문제에 봉착했습니다. 여기 내 해결책이 있습니다.

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는 모든 프리 페치를 제거하는 빠르고 더러운 방법입니다. 후속 다 대다 액세스는 프리 페치가없는 것처럼 작동합니다. 밑줄로 시작하는 문서화되지 않은 속성을 사용하는 것은 호환성 문제를 요구할 수도 있지만 현재로서는 효과가 있습니다.


나는 일이를 얻을 수 있었다, 그러나 나는라는 속성했다대로 이미 1.11에서 변경 될 수 있습니다처럼 보이는 _[model_name]_cache, 삭제하면, 내가 전화를 한 후, 그 관련 모델에 새로운 ID를 할당 할 수 있었다 save(). 아직 결정하지 않은 부작용이 여전히있을 수 있습니다.
trpt4him

클래스 / 믹스 인의 함수에서 복제를 수행하는 경우 매우 중요한 정보입니다. 그렇지 않으면 '자기'가 엉망이되어 혼란 스러울 수 있습니다.
Andreas Bergström

5

pk를 None으로 설정하는 것이 좋습니다. sinse Django가 pk를 올바르게 만들 수 있습니다.

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

3

이것은 모델 인스턴스를 복제하는 또 다른 방법입니다.

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

0

상속 수준이 여러 개인 모델 (예 :> = 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)

여기 에서 질문을 참조 하십시오 .


아 네,하지만 그 질문에는 대답이 없습니다! 잘 했어!
Bobort

0

이 시도

original_object = Foo.objects.get(pk="foo")
v = vars(original_object)
v.pop("pk")
new_object = Foo(**v)
new_object.save()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.