Foo.objects.get(pk="foo")
<Foo: test>
データベースに、上記のオブジェクトのコピーである別のオブジェクトを追加します。
テーブルに1つの行があるとします。別の主キーを持つ別の行に最初の行オブジェクトを挿入したい。どうやってやるの?
オブジェクトの主キーを変更し、save()を実行するだけです。
obj = Foo.objects.get(pk=<some_existing_pk>)
obj.pk = None
obj.save()
自動生成キーが必要な場合は、新しいキーを[なし]に設定します。
UPDATE/INSERTの詳細 こちら 。
データベースクエリの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
関数を追加することについての議論もありましたが、私が知る限り、彼らはまだその問題に取り組まないことに決めました。したがって、この「手動」のコピー方法は、おそらく今のところ実行する必要があります。
ここで注意してください。これは、何らかのループが発生し、オブジェクトを1つずつ取得する場合、非常に高価になる可能性があります。データベースへの呼び出しが必要ない場合は、次のようにします。
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)
クローンスニペット 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)
これを行う方法は、Django1.4の公式Djangoドキュメントに追加されました
https://docs.djangoproject.com/en/1.10/topics/db/queries/#copying-model-instances
公式の答えはmiahの答えに似ていますが、ドキュメントは継承と関連オブジェクトのいくつかの困難を指摘しているので、ドキュメントを必ず読んでください。
pkをNoneに設定する方が良いです。sinseDjangoはpkを正しく作成できます
object_copy = MyObject.objects.get(pk=...)
object_copy.pk = None
object_copy.save()
私は受け入れられた答えでいくつかの落とし穴に遭遇しました。これが私の解決策です。
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
を削除すると、すべてのプリフェッチをすばやく削除できます。後続の多対多アクセスは、プリフェッチがなかったように機能します。アンダースコアで始まる文書化されていないプロパティを使用すると、おそらく互換性の問題が発生しますが、現時点では機能します。
これを試して
original_object = Foo.objects.get(pk="foo")
v = vars(original_object)
v.pop("pk")
new_object = Foo(**v)
new_object.save()
これは、モデルインスタンスを複製するもう1つの方法です。
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)
質問 here を参照してください。