2010-01-10 7 views

Antwort

30

Eigentlich ist obj.pk der kanonischste Weg. Django selbst weiß oft nicht, ob das Objekt gespeichert ist oder nicht. Laut the django model instance reference, wenn bereits ein Primärschlüssel festgelegt wurde, werden die Aufrufe save() überprüft, indem vor der Einfügung für die ID in der Datenbank ausgewählt wird.

Auch wenn Sie setzen, zeigt das Attribut .pk immer noch auf den richtigen Primärschlüssel (höchstwahrscheinlich user_id) und Sie können es verwenden und festlegen, als ob es die gleiche Eigenschaft wäre. Wenn Sie wissen möchten, nachdem ein Objekt gespeichert wurde, können Sie die post_save signal fangen. Dieses Signal wird bei Modellspeicherungen ausgelöst. Wenn Sie möchten, können Sie dem Modell ein eigenes anwendungsspezifisches Attribut hinzufügen, z. B. obj.was_saved = True. Ich denke, Django vermeidet dies, um ihre Instanzen sauber zu halten, aber es gibt keinen wirklichen Grund, warum du das nicht für dich selbst tun könntest. Hier ist ein minimales Beispiel:

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) 

Sie abwechselnd diese Funktion Arbeit für alle Modelle in Ihrer Anwendung durch einfaches Verbinden des Signals ohne Angabe des sender= Argument haben. Beachten Sie jedoch, dass Sie nicht definierte Verhalten erstellen können, wenn Sie eine Eigenschaft in der Modellinstanz einer anderen Person, die Sie importieren, überschreiben.

+14

Es gibt standardmäßig einen booleschen kwarg 'created' im Post-Speichern-Signal, so dass Sie keine eigenen einfügen müssen. – kibitzer

+0

Für modernes Django sollte beachtet werden, dass dies nicht funktioniert, wenn Sie Ihre eigenen Primärschlüsselfelder mit einem Standardwertgenerator wie einem UUIDField oder einer anderen Methode zum Generieren von IDs verwenden. Wie in anderen Antworten erwähnt, sollten Sie 'self._state.adding' verwenden. https://stackoverflow.com/a/19379636/23972 –

6

Beispiel obj ist eine Instanz von MyModel. Dann könnten wir den folgenden Code-Block verwenden, um zu überprüfen, ob es bereits eine Instanz mit dem Primärschlüssel in der Datenbank:

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 

Das ist besser als ob obj.pk is None Kontrolle, weil Sie tun könnten

obj = MyModel() 
obj.pk = 123 

dann

obj.pk is None # False 

Dies ist sogar sehr wahrscheinlich, wenn Sie nicht über das autoincrement id Feld als Primärschlüssel verwenden, sondern ein natürlichen i nstead.

Oder, wie Matthew in den Kommentaren darauf hingewiesen, Sie

obj.delete() 

wonach haben Sie immer noch gut

obj.pk is None # False 
+1

Diese Antwort half mir b/c Ich fand, dass .pk noch nach einem delete() eingestellt wurde. Vielen Dank. –

3

@ Crast Antwort tun konnte, war, aber ich denke, unvollständig. Der Code, den ich in meiner Einheit verwende, um festzustellen, ob ein Objekt in der Datenbank ist, ist wie folgt. Darunter werde ich erklären, warum ich denke, es ist besser als zu überprüfen, ob obj.pk is None.

Meine Lösung

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) 

Hinweise: den obigen Code mit Vorsicht walten lassen, wenn Sie benutzerdefinierte Manager auf Ihrem Modelle Name etwas anderes als objects verwenden. (Ich bin sicher, es gibt eine Möglichkeit, Django dazu zu bringen, Ihnen zu sagen, was der Standardmanager ist.) Außerdem weiß ich, dass /assert(Not)?InDB/ keine PEP 8-Methodennamen sind, aber ich habe den Stil des restlichen unittest Pakets verwendet.

Begründung

Der Grund, warum ich denke, assertInDB(obj) als assertIsNotNone(obj.pk) besser ist, weil der folgende Fall. Angenommen, Sie haben das folgende Modell.

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

Node Modelle eine doppelt verknüpfte Liste: Sie beliebige Daten an jeden Knoten mit Fremdschlüssel anbringen kann und der Schwanz ist die Nodeobj so dass obj.next is None. Standardmäßig fügt Django die SQL-Einschränkung ON DELETE CASCADE dem Primärschlüssel Node hinzu. Nun nehme an, Sie haben eine listKnoten der Länge n so dass nodes[i].next == nodes[i + 1] für i in [0, n-1). Angenommen, Sie rufen nodes[0].delete(). In meinen Tests auf Django 1.5.1 auf Python 3.3 fand ich, dass nodes[i].pk is not None für i in [1, n) und nur nodes[0].pk is None. Jedoch haben meine obigen /assert(Not)?InDB/ Methoden korrekt erkannt, dass nodes[i] für i in [1, n] tatsächlich gelöscht worden war.

+0

Dies gilt für alle Versionen von Django und Python, da die Elemente in Ihrer 'nodes'-Liste instanziierte Modellobjekte sind und daher nicht mehr von irgendetwas betroffen sind, das in der Datenbank passiert. Die Überprüfung auf 'pk is not None' sagt Ihnen zumindest, ob die Instanz zu irgendeinem Zeitpunkt * in der db gespeichert wurde (... vorausgesetzt, niemand hat' pk' auf der Instanz manuell einen Wert zugewiesen) – Anentropic

+0

Eine bessere Alternative für 'assertInDb' wäre:' obj.refresh_from_db() 'und für' assertNotInDb': 'mit self.assertRaises (obj.DoesNotExist): obj.refresh_from_db()'. –

20

Heutzutage kann man prüfen:

self._state.adding 

Dieser Wert wird von den QuerySet.iterator() für Objekte festgelegt ist, die noch nicht in der Datenbank hinzugefügt werden. Sie können diesen Wert in der __init__()-Methode noch nicht verwenden, da er nach dem Erstellen des Objekts festgelegt wurde.

+1

Danke, ich wusste nicht, dass es existiert auf Django 1.8 gearbeitet (Python 3.x) –

+3

Es wird noch besser in Django 1.8, jetzt gibt es 'Model.from_db()' – vdboor

+5

Dies sollte die derzeit akzeptierte Antwort sein, weil zu prüfen Der Primärschlüssel funktioniert nicht, wenn Sie UUID-Felder verwenden, da diese beim Erstellen einer Instanz automatisch generiert werden. – nemesisdesign

Verwandte Themen