web-dev-qa-db-ja.com

Djangoモデルインスタンスオブジェクトを複製してデータベースに保存するにはどうすればよいですか?

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

データベースに、上記のオブジェクトのコピーである別のオブジェクトを追加します。

テーブルに1つの行があるとします。別の主キーを持つ別の行に最初の行オブジェクトを挿入したい。どうやってやるの?

226
user426795

オブジェクトの主キーを変更し、save()を実行するだけです。

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

自動生成キーが必要な場合は、新しいキーを[なし]に設定します。

UPDATE/INSERTの詳細 こちら

380
miah

データベースクエリの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()が元のオブジェクトを作成し、2番目のsave()がコピーを作成します。

ドキュメントを読み続けると、さらに2つの複雑なケースを処理する方法の例もあります。(1)モデルサブクラスのインスタンスであるオブジェクトのコピー、および(2)多目的オブジェクトを含む関連オブジェクトのコピー-多くの関係。


Miahの回答に関する注意:pkをNoneに設定することはmiahの回答で言及されていますが、正面と中央には表示されません。したがって、私の答えは、主にその方法をDjangoが推奨する方法として強調するのに役立ちます。

歴史的なメモ:これは、バージョン1.4までDjangoのドキュメントで説明されていませんでした。ただし、1.4以前から可能でした。

可能な将来の機能:前述のドキュメントの変更は、 this ticket で行われました。チケットのコメントスレッドでは、モデルクラスに組み込みのcopy関数を追加することについての議論もありましたが、私が知る限り、彼らはまだその問題に取り組まないことに決めました。したがって、この「手動」のコピー方法は、おそらく今のところ実行する必要があります。

123
S. Kirby

ここで注意してください。これは、何らかのループが発生し、オブジェクトを1つずつ取得する場合、非常に高価になる可能性があります。データベースへの呼び出しが必要ない場合は、次のようにします。

from copy import deepcopy

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

これらの他の回答のいくつかと同じことをしますが、オブジェクトを取得するためのデータベース呼び出しを行いません。これは、データベースにまだ存在しないオブジェクトのコピーを作成する場合にも役立ちます。

41
Troy Grosfield

以下のコードを使用してください:

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)
22
t_io

クローンスニペット 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)
20
Dominic Rodger

これを行う方法は、Django1.4の公式Djangoドキュメントに追加されました

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

公式の答えはmiahの答えに似ていますが、ドキュメントは継承と関連オブジェクトのいくつかの困難を指摘しているので、ドキュメントを必ず読んでください。

19
Michael Bylstra

pkをNoneに設定する方が良いです。sinseDjangoはpkを正しく作成できます

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

私は受け入れられた答えでいくつかの落とし穴に遭遇しました。これが私の解決策です。

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モデルインスタンスの浅いコピーを望ましい方法で生成するようです。これは文書化されていなかったものの1つですが、酸洗と酸洗によって機能するため、おそらく十分にサポートされています。

次に、承認された回答により、プリフェッチされた結果が新しいインスタンスに添付されたままになります。多対多のリレーションシップを明示的にコピーしない限り、これらの結果を新しいインスタンスに関連付けることはできません。プリフェッチされた関係をトラバースすると、データベースと一致しない結果が得られます。プリフェッチを追加するときに動作中のコードを壊すことは、意外なことです。

_prefetched_objects_cacheを削除すると、すべてのプリフェッチをすばやく削除できます。後続の多対多アクセスは、プリフェッチがなかったように機能します。アンダースコアで始まる文書化されていないプロパティを使用すると、おそらく互換性の問題が発生しますが、現時点では機能します。

3
morningstar

これを試して

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

これは、モデルインスタンスを複製するもう1つの方法です。

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

複数の継承レベル(> = 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 を参照してください。

0
David Cheung