Django datetime Probleme (Standard = datetime.now ())

283

Ich habe das folgende DB-Modell:

from datetime import datetime    

class TermPayment(models.Model):
    # I have excluded fields that are irrelevant to the question
    date = models.DateTimeField(default=datetime.now(), blank=True)

Ich füge eine neue Instanz hinzu, indem ich Folgendes verwende:

tp = TermPayment.objects.create(**kwargs)

Mein Problem: Alle Datensätze in der Datenbank haben den gleichen Wert im Datumsfeld, dem Datum der ersten Zahlung. Nach dem Neustart des Servers hat ein Datensatz das neue Datum und die anderen Datensätze das gleiche wie der erste. Es sieht so aus, als ob einige Daten zwischengespeichert sind, aber ich kann nicht finden, wo.

Datenbank: MySQL 5.1.25

django v1.1.1

Shamanu4
quelle
1
Ist es nicht möglich, standardmäßig eine Funktion wie diese zu verwenden?: default=datetime.now- Hinweis, ohne Aufruf wie in now()Nicht der Standard für DateTimeField, aber ... auf jeden Fall praktisch.
Viridis

Antworten:

597

Es sieht so aus, datetime.now()als würde es ausgewertet, wenn das Modell definiert wird, und nicht jedes Mal, wenn Sie einen Datensatz hinzufügen.

Django hat eine Funktion, mit der Sie das erreichen können, was Sie bereits versuchen:

date = models.DateTimeField(auto_now_add=True, blank=True)

oder

date = models.DateTimeField(default=datetime.now, blank=True)

Der Unterschied zwischen dem zweiten Beispiel und dem, was Sie derzeit haben, ist das Fehlen von Klammern. Wenn Sie datetime.nowohne Klammern übergeben, übergeben Sie die eigentliche Funktion, die jedes Mal aufgerufen wird, wenn ein Datensatz hinzugefügt wird. Wenn Sie es übergeben datetime.now(), bewerten Sie nur die Funktion und übergeben ihr den Rückgabewert.

Weitere Informationen sind unter Djangos verfügbar Modellfeld Referenz

Carson Myers
quelle
206
Wichtiger Hinweis : Die Verwendung von auto_now_add macht das Feld im Administrator nicht mehr bearbeitbar
Jiaaro
7
Tolle Antwort, danke. Gibt es eine Möglichkeit, einen komplexeren Ausdruck mit datetime.now als Standard auszudrücken? zB jetzt + 30 Tage (das Folgende funktioniert nicht) expiry = models.DateTimeField(default=datetime.now + timedelta(days=30))
michela
26
@michela datetime.now ist eine Funktion, und Sie versuchen, einer Funktion ein Zeitdelta hinzuzufügen. Sie müssen Ihren eigenen Rückruf definieren, um den Standardwert festzulegen def now_plus_30(): return datetime.now() + timedelta(days = 30), und dannmodels.DateTimeField(default=now_plus_30)
Carson Myers
55
Oder Sie können natürlich tundefault=lambda: datetime.now()+timedelta(days=30)
Michael Mior
34
BE AWARE dass auto_now_add Verwendung ist nicht als mit einem Standard gleich , weil das Feld immer wird gleich jetzt (nicht editierbar) .. Ich weiß , dass dies schon gesagt, aber es ist nicht nur ein metter der ‚admin‘ Seite
VAShhh
114

Anstatt zu verwenden datetime.now, sollten Sie wirklich verwendenfrom django.utils.timezone import now

Referenz:

Also mach so etwas:

from django.utils.timezone import now


created_date = models.DateTimeField(default=now, editable=False)
Andilabs
quelle
13
Dies ist ein sehr wichtiger Hinweis! Wenn Sie datetime.now verwenden, können Sie eine lokale datetime verwenden, die keine Zeitzone kennt. Wenn Sie es später mit einem Feld vergleichen, das mit einem Zeitstempel versehen wurde auto_now_add(was zeitzonenabhängig ist), können Sie aufgrund schwerwiegender Zeitzonenunterschiede falsche Berechnungen erhalten.
Iftah
1
Dies ist definitiv sehr wichtig, beantwortet aber die Frage nicht wirklich von selbst.
Czechnology
1
definitiv besserer Weg
Carmine Tambascia
28

Aus der Dokumentation zum Standardfeld des Django-Modells:

Der Standardwert für das Feld. Dies kann ein Wert oder ein aufrufbares Objekt sein. Wenn aufrufbar, wird es jedes Mal aufgerufen, wenn ein neues Objekt erstellt wird.

Daher sollte Folgendes funktionieren:

date = models.DateTimeField(default=datetime.now,blank=True)
Mykhal
quelle
1
Dies ist nur in Django 1.8+ möglich
David Schumann
@ DavidNathan warum ist das so? Ich denke, beide Optionen gibt es seit 1.4 oder früher, einschließlich der Verwendung eines Callable für default( django-chinese-docs-14.readthedocs.io/en/latest/ref/models/… )
Mark
16

David hatte die richtige Antwort. Die Klammer () bewirkt, dass die aufrufbare Zeitzone.now () jedes Mal aufgerufen wird, wenn das Modell ausgewertet wird. Wenn Sie () aus timezone.now () (oder datetime.now () entfernen, wenn Sie das naive datetime-Objekt verwenden), um genau dies zu erreichen:

default=timezone.now

Dann funktioniert es wie erwartet:
Neue Objekte erhalten das aktuelle Datum, wenn sie erstellt werden, aber das Datum wird nicht jedes Mal überschrieben, wenn Sie verwalten.py makemigrations / migrate ausführen.

Ich bin gerade darauf gestoßen. Vielen Dank an David.

MicahT
quelle
10

Das datetime.now()wird beim Erstellen der Klasse ausgewertet, nicht beim Hinzufügen eines neuen Datensatzes zur Datenbank.

Um das zu erreichen, was Sie wollen, definieren Sie dieses Feld als:

date = models.DateTimeField(auto_now_add=True)

Auf diese Weise wird das dateFeld für jeden neuen Datensatz auf das aktuelle Datum gesetzt.

Bartosz
quelle
6

Die Antwort auf diese Frage ist tatsächlich falsch.

Automatisches Ausfüllen des Werts (auto_now / auto_now_add ist nicht dasselbe wie Standard). Der Standardwert ist tatsächlich das, was der Benutzer sieht, wenn es sich um ein brandneues Objekt handelt. Was ich normalerweise mache, ist:

date = models.DateTimeField(default=datetime.now, editable=False,)

Stellen Sie sicher, dass Sie, wenn Sie versuchen, dies auf einer Admin-Seite darzustellen, diese als "read_only" auflisten und auf den Feldnamen verweisen

read_only = 'date'

Ich mache dies erneut, da mein Standardwert normalerweise nicht bearbeitet werden kann und Admin-Seiten nicht bearbeitbare Elemente ignorieren, sofern nicht anders angegeben. Es gibt jedoch sicherlich einen Unterschied zwischen dem Festlegen eines Standardwerts und dem Implementieren von auto_add, was hier der Schlüssel ist. Probieren Sie es aus!

Andrew Harris
quelle
6

datetime.now()wird einmal ausgewertet, wenn Ihre Klasse instanziiert wird. Entfernen Sie die Klammern, damit die Funktion datetime.nowzurückgegeben und dann ausgewertet wird. Ich hatte das gleiche Problem beim Festlegen von Standardwerten für mein DateTimeFields und schrieb meine Lösung hier auf .

David
quelle
5

Aus der Python-Sprachreferenz unter Funktionsdefinitionen :

Standardparameterwerte werden ausgewertet, wenn die Funktionsdefinition ausgeführt wird. Dies bedeutet, dass der Ausdruck einmal ausgewertet wird, wenn die Funktion definiert ist, und dass für jeden Aufruf derselbe „vorberechnete“ Wert verwendet wird.

Glücklicherweise hat Django eine Möglichkeit, das zu tun, was Sie wollen, wenn Sie das auto_nowArgument für Folgendes verwenden DateTimeField:

date = models.DateTimeField(auto_now=True)

Weitere Informationen zu DateTimeField finden Sie in den Django-Dokumenten .

ars
quelle
0

In Django 3.0 auto_now_addscheint das zu funktionierenauto_now

reg_date=models.DateField(auto_now=True,blank=True)

Bosco Oludhe
quelle