Foo.objects.get(pk="foo")
<Foo: test>
データベースに、上記のオブジェクトのコピーである別のオブジェクトを追加します。
テーブルに1行あるとします。最初の行オブジェクトを別の主キーを持つ別の行に挿入したい。どうやってやるの?
Foo.objects.get(pk="foo")
<Foo: test>
データベースに、上記のオブジェクトのコピーである別のオブジェクトを追加します。
テーブルに1行あるとします。最初の行オブジェクトを別の主キーを持つ別の行に挿入したい。どうやってやるの?
回答:
オブジェクトの主キーを変更して、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
obj.pk
ありobj.id
ました
データベースクエリに関する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()
作成し、2番目のオブジェクトがコピーを作成します。
ドキュメントを読み続けると、2つのより複雑なケースを処理する方法の例もあります:(1)モデルサブクラスのインスタンスであるオブジェクトのコピー、および(2)多対多のオブジェクトを含む関連オブジェクトのコピー-多くの関係。
miahの回答に関する注意:pkをに設定することNone
はmiahの回答に記載されていますが、正面と中央には示されていません。だから私の答えは、主にその方法をDjangoが推奨する方法として強調するのに役立ちます。
歴史的注記:これはバージョン1.4までDjangoのドキュメントでは説明されていませんでした。しかし、それは1.4以前から可能でした。
可能な将来の機能:前述のドキュメントの変更は、このチケットで行われました。チケットのコメントスレッドで、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)
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
ます。
これを行う方法は、Django1.4の公式Djangoドキュメントに追加されました
https://docs.djangoproject.com/en/1.10/topics/db/queries/#copying-model-instances
公式の答えはミアの答えに似ていますが、ドキュメントは継承と関連オブジェクトのいくつかの困難を指摘しているので、おそらくドキュメントを必ず読んでください。
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モデルインスタンスの浅いコピーを希望どおりに生成するようです。これはドキュメントに記載されていなかったものの1つですが、酸洗いと酸洗いによって機能するため、おそらく十分にサポートされています。
次に、承認された回答は、プリフェッチされた結果を新しいインスタンスに添付したままにします。多対多の関係を明示的にコピーしない限り、これらの結果を新しいインスタンスに関連付けることはできません。プリフェッチされた関係をトラバースすると、データベースと一致しない結果が得られます。プリフェッチを追加するときに作業コードを壊すのは、意外なことです。
削除_prefetched_objects_cache
は、すべてのプリフェッチを取り除く簡単な方法です。後続の多くのアクセスは、プリフェッチがなかったかのように機能します。アンダースコアで始まる文書化されていないプロパティを使用すると、互換性の問題が発生する可能性がありますが、今のところ機能します。
_[model_name]_cache
。これは、削除されると、その関連モデルに新しいIDを割り当てて、を呼び出すことができましたsave()
。まだ決定していない副作用があるかもしれません。
複数の継承レベル、つまり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)
こちらの質問を参考にしてください。