web-dev-qa-db-ja.com

Djangoモデルがデータベースに保存されているかどうかを確認するための正規の方法は何ですか?

オブジェクトが保存されている場合は、通常、_if obj.pk_をチェックしてknwoをチェックします。ただし、一部のフィールドに_primary_key = True_を設定している場合、これは機能しません。たとえば、UserProfileuser = models.OneToOneField(User, primary_key=True)を設定します。

Djangoモデルがデータベースに保存されているかどうかを確認するための正規の方法は何ですか?

40
agiliq

重要な注意事項(19年5月6日現在):モデルでUUIDフィールド(またはその他の内部ID生成方法)を使用している場合は、コメントに記載されているように_self._state.adding_を使用してください。

実際には、_obj.pk_が最も標準的な方法です。 Djangoオブジェクト自体が保存されているかどうかは、多くの場合それ自体が「認識」していません。 the Djangoモデルインスタンス参照 によると、主キーセットが既にある場合は、挿入する前にデータベース内のIDを選択して、onsave()呼び出しをチェックします。

user = models.OneToOneField(..., primary_key=True)を設定しても、_.pk_属性は正しい主キー(おそらく_user_id_)をポイントし、同じプロパティであるかのように使用して設定できます。

オブジェクトが保存された後に知りたい場合は、 post_saveシグナル をキャッチできます。この信号はモデルの保存時に発生し、必要に応じて、_obj.was_saved = True_などの独自のアプリケーション固有の属性をモデルに追加できます。 Djangoはインスタンスをクリーンに保つためにこれを回避しますが、これを自分で実行できなかった理由はありません。以下に最小限の例を示します。

_from Django.db.models.signals import post_save
from myapp.models import MyModel

def save_handler(sender, instance, **kwargs):
    instance.was_saved = True

post_save.connect(save_handler, sender=MyModel)
_

あるいは、_sender=_引数を指定せずに信号を接続するだけで、アプリ内のallモデルに対してこの関数を機能させることができます。ただし、インポートする他の誰かのモデルインスタンスのプロパティをオーバーライドすると、未定義の動作を作成する可能性があります。

35
Crast

現在、次のことを確認できます。

_self._state.adding
_

この値は、データベースにまだ追加されていないオブジェクトのQuerySet.iterator()によって設定されます。この値は、オブジェクトの構築後に設定されるため、__init__()メソッドではまだ使用できません。

34
vdboor

objMyModelのインスタンスであるとしましょう。次に、次のコードブロックを使用して、データベースにその主キーを持つインスタンスが既に存在するかどうかを確認します。

if obj.pk is None:
    # Definitely doesn't exist, since there's no `pk`.
    exists = False
else:
    # The `pk` is set, but it doesn't guarantee exists in db.
    try:
        obj_from_db = MyModel.objects.get(pk=obj.pk)
        exists = True
    except MyModel.DoesNotExist:
        exists = False

これはobj.pk is None、あなたができるから

obj = MyModel()
obj.pk = 123

その後

obj.pk is None  # False

これは、自動インクリメントidフィールドを主キーとして使用せず、代わりに自然キーを使用する場合でも、非常に可能性が高くなります。

または、マシューがコメントで指摘したように、

obj.delete()

その後、あなたはまだ持っています

obj.pk is None  # False
10
Rubinous

@キャストの答えは良かったが、私は不完全だと思う。単体テストでオブジェクトがデータベースにあるかどうかを判断するために使用するコードは、次のとおりです。その下で、_obj.pk is None_のチェックよりも優れていると思う理由を説明します。

私の解決策

_from Django.test import TestCase
class TestCase(TestCase):
    def assertInDB(self, obj, msg=None):
        """Test for obj's presence in the database."""
        fullmsg = "Object %r unexpectedly not found in the database" % obj
        fullmsg += ": " + msg if msg else ""
        try:
            type(obj).objects.get(pk=obj.pk)
        except obj.DoesNotExist:
            self.fail(fullmsg)

    def assertNotInDB(self, obj, msg=None):
        """Test for obj's absence from the database."""
        fullmsg = "Object %r unexpectedly found in the database" % obj
        fullmsg += ": " + msg if msg else ""
        try:
            type(obj).objects.get(pk=obj.pk)
        except obj.DoesNotExist:
            return
        else:
            self.fail(fullmsg)
_

注:モデル名にobjects以外のカスタムマネージャーを使用する場合は、上記のコードを慎重に使用してください。 (Djangoを取得して、デフォルトのマネージャーが何であるかを知る方法があると確信しています。)さらに、/assert(Not)?InDB/はPEP 8メソッド名ではないことを知っています。しかし、私はunittestパッケージの残りが使用するスタイルを使用しました。

正当化

assertInDB(obj)assertIsNotNone(obj.pk)よりも優れていると思う理由は、次のような場合です。次のモデルがあるとします。

_from Django.db import models
class Node(models.Model):
    next = models.OneToOneField('self', null=True, related_name='prev')
_

Nodeは二重リンクリストをモデル化します。外部キーを使用して各ノードに任意のデータをアタッチでき、末尾はNodeobjであり、_obj.next is None_となります。デフォルトでは、DjangoはSQL制約_ON DELETE CASCADE_をNodeの主キーに追加します。ここで、listノード長さn _nodes[i].next == nodes[i + 1]_ for i in [0、n-1)。 nodes[0].delete()を呼び出すとします。 Django 1.5.1のPython 3.3でのテストでは、_nodes[i].pk is not None_ for i for [1 、n)および_nodes[0].pk is None_のみ。ただし、上記の/assert(Not)?InDB/メソッドは、[1のiの_nodes[i]_を正しく検出しました、n)は実際に削除されました。

3
wkschwartz