djangoモデルのカスタムsave()メソッドでは、新しいオブジェクトをどのように識別する必要がありますか?


172

新しいレコードを保存する(既存のレコードを更新しない)ときに、Djangoモデルオブジェクトのsave()メソッドで特別なアクションをトリガーしたい。

(self.id!= None)のチェックは、セルフレコードが新しく、更新されていないことを保証するために必要かつ十分ですか?これが見落とす可能性のある特別な場合はありますか?


正解としてstackoverflow.com/a/35647389/8893667を選択してください。次のような多くの場合、答えは機能しませんUUIDField pk
Kotlinboy '10

回答:


204

更新:self._stateプライベートインスタンス変数ではないが、競合を回避するためにそのように名前が付けられた明確化により、チェックself._state.addingはチェックするための望ましい方法です。


self.pk is None:

オブジェクトが持っていない限り、新しいモデルオブジェクト内にTrueを返しUUIDField、そのようprimary_key

あなたが心配しなければならないかもしれない主なケースは、ID以外のフィールドに一意性の制約があるかどうかです(たとえば、他のフィールドのセカンダリ一意インデックス)。その場合、まだ新しいレコードを持っている可能性がありますが、それを保存することはできません。


20
オブジェクトとの同一性をチェックするときis notではなく、使用する必要!=がありNoneます
Ben James

3
すべてのモデルにid属性があるわけではありませんmodels.OneToOneField(OtherModel, primary_key=True)。つまり、を介して別のモデルが拡張されています。使用する必要があると思いますself.pk
AJP

4
これは機能しない場合があります。この回答を確認してください:stackoverflow.com/a/940928/145349
fjsj

5
これは正解ではありません。UUIDFieldaを主キーとして使用する場合、self.pk決してではありませんNone
Daniel van Flymen 2016年

1
補足:この回答は、日付が古いUUIDFieldです。
Dave W. Smith

190

モデルをself.pkチェックできるチェックself._stateの代替方法

self._state.adding is True 作成

self._state.adding is False 更新

このページから入手した


12
これは、カスタム主キーフィールドを使用する場合の唯一の正しい方法です。
webtweakers

9
self._state.adding動作の詳細についてはよくわかりませんが呼び出し後にFalse確認すると常に等しいように見えるという警告があります:github.com/django/django/blob/stable/1.10.x/django/db/models/ …super(TheModel, self).save(*args, **kwargs)
agilgur5 2016年

1
これは正しい方法であり、正解として賛成/設定する必要があります。
flungo 2017年

7
@guival:_state非公開ではありません。のように_meta、フィールド名との混同を避けるために、アンダースコアが前に付けられます。(リンクされたドキュメントでの使用方法に注意してください。)
Ry-

2
これが最良の方法です。私is_new = self._state.addingはそれsuper(MyModel, self).save(*args, **kwargs)から、そしてそれからif is_new: my_custom_logic()
kotrfa 2017


39

のチェックでself.pk == Noneは、オブジェクトがデータベースに挿入または更新されるかどうかを判断するに不十分です。

Django O / RMは特に厄介なハックを特徴としており、基本的にはPKの位置に何かがあるかどうかを確認し、ある場合はUPDATEを実行します。

これを行う必要があるのは、オブジェクトの作成時にPKを設定できるためです。主キーのシーケンス列がある場合は一般的ではありませんが、これは他のタイプの主キーフィールドには適用されません。

本当に知りたい場合は、O / RMが行うことを実行して、データベースを調べる必要があります。

もちろん、コードに特定のケースがあり、そのためself.pk == Noneに知っておく必要があるすべてのことを示している可能性が高いですが、それは一般的な解決策ではありません


いい視点ね!新しいオブジェクトにpkを設定したことがないので、アプリケーションでこれを回避できます(None主キーをチェックしています)。しかし、これは間違いなく再利用可能なプラグインやフレームワークの一部の良いチェックではありません。
MikeN、2009年

1
これは、主キーを自分で、およびデータベースを通じて割り当てる場合に特に当てはまります。その場合、最も確実なことは、dbにアクセスすることです。
コンスタンティンM

1
アプリケーションコードが明示的にpksを指定していない場合でも、テストケースのフィクスチャはそうする可能性があります。ただし、テストの前に通常ロードされるため、問題にはならない可能性があります。
リサディナ2014年

1
これはUUIDField、aを主キーとして使用する場合に特に当てはまります。キーはDBレベルで入力されないためself.pk、常にTrueです。
Daniel van Flymen 2016年

10

「作成された」クワーグを送信するpost_save信号に接続するだけで、trueの場合、オブジェクトが挿入されています。

http://docs.djangoproject.com/en/stable/ref/signals/#post-save


8
負荷が多い場合、競合状態が発生する可能性があります。これは、post_saveシグナルが保存時に送信されるためですが、トランザクションがコミットされる前です。これには問題があり、デバッグが非常に困難になる可能性があります。
Abel Mohler、2012年

(古いバージョンから)変更があったかどうかはわかりませんが、シグナルハンドラーが同じトランザクション内で呼び出されるため、どこかでエラーが発生するとトランザクション全体がロールバックされます。私はを使用ATOMIC_REQUESTSしているので、デフォルトについて本当にわかりません。
Tim Tisdall、2017年

7

self.idforce_insertフラグを確認してください。

if not self.pk or kwargs.get('force_insert', False):
    self.created = True

# call save method.
super(self.__class__, self).save(*args, **kwargs)

#Do all your post save actions in the if block.
if getattr(self, 'created', False):
    # So something
    # Do something else

新しく作成したオブジェクト(self)にpk値があるため、これは便利です


5

この会話に非常に遅れていますが、デフォルト値が関連付けられているときにself.pkにデータが入力されるという問題に遭遇しました。

これを回避する方法は、モデルにdate_createdフィールドを追加することです

date_created = models.DateTimeField(auto_now_add=True)

ここから行けます

created = self.date_created is None


4

UUIDField主キーとして使用している場合でも機能するソリューション(他の人が指摘しているように、Noneオーバーライドするだけではそうではない)の場合save、Djangoのpost_saveシグナルにプラグインできます。これをmodels.pyに追加します:

from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=MyModel)
def mymodel_saved(sender, instance, created, **kwargs):
    if created:
        # do extra work on your instance, e.g.
        # instance.generate_avatar()
        # instance.send_email_notification()
        pass

このコールバックはsaveメソッドをブロックするため、フォームを使用している場合でも、AJAX呼び出しにDjango RESTフレームワークを使用している場合でも、応答を送信する前に、トリガー通知やモデルの更新などを行うことができます。もちろん、ユーザーを待たせるのではなく、責任を持って使用し、重いタスクをジョブキューにオフロードしてください:)




0

これは上記のすべてのシナリオで機能しますか?

if self.pk is not None and <ModelName>.objects.filter(pk=self.pk).exists():
...

これにより、追加のデータベースヒットが発生します。
David Schumann、

0
> def save_model(self, request, obj, form, change):
>         if form.instance._state.adding:
>             form.instance.author = request.user
>             super().save_model(request, obj, form, change)
>         else:
>             obj.updated_by = request.user.username
> 
>             super().save_model(request, obj, form, change)

Clean_data.get()を使用して、インスタンスがあるかどうかを判断できました。また、CharFieldがnullで、trueの場合は空白でした。ログインしたユーザーに応じて、更新ごとに更新されます
Swelan Auguste

-3

オブジェクト(データ)を更新するのか挿入するのかを知るにself.instance.fieldnameは、フォームで使用します。フォームにクリーンな関数を定義し、現在の値のエントリが前と同じかどうかを確認します。そうでない場合は、更新します。

self.instanceself.instance.fieldname新しい値と比較します

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.