SQLAlchemy Standard DateTime

174

Dies ist mein deklaratives Modell:

import datetime
from sqlalchemy import Column, Integer, DateTime
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Test(Base):
    __tablename__ = 'test'

    id = Column(Integer, primary_key=True)
    created_date = DateTime(default=datetime.datetime.utcnow)

Wenn ich jedoch versuche, dieses Modul zu importieren, wird folgende Fehlermeldung angezeigt:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "orm/models2.py", line 37, in <module>
    class Test(Base):
  File "orm/models2.py", line 41, in Test
    created_date = sqlalchemy.DateTime(default=datetime.datetime.utcnow)
TypeError: __init__() got an unexpected keyword argument 'default'

Wenn ich einen Integer-Typ verwende, kann ich einen Standardwert festlegen. Was ist los?

Brandon O'Rourke
quelle
1
Dies sollte nicht verwendet werden. utcnow ist ein naiver Zeitstempel in der UTC-Zeitzone. Es besteht jedoch die Möglichkeit, dass naive Zeitstempel stattdessen in der lokalen Zeitzone interpretiert werden.
Antti Haapala
1
Ich weiß, dass diese Frage vor langer Zeit gestellt wurde, aber ich denke, Ihre Antwort sollte in die Antwort geändert werden, die @Jeff Widman bereitgestellt hat, da die andere die "Kompilierungs" -Zeit datetime verwendet, wenn die Tabellenklasse definiert wird, im Vergleich zum Zeitpunkt, an dem der Datensatz erstellt wird. Wenn Sie AFK sind, gibt zumindest dieser Kommentar eine Warnung für andere aus, die Kommentare für jede Frage sorgfältig zu überprüfen.
David

Antworten:

186

DateTimehat keinen Standardschlüssel als Eingabe. Die Standardtaste sollte eine Eingabe für die ColumnFunktion sein. Versuche dies:

import datetime
from sqlalchemy import Column, Integer, DateTime
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Test(Base):
    __tablename__ = 'test'

    id = Column(Integer, primary_key=True)
    created_date = Column(DateTime, default=datetime.datetime.utcnow)
PearsonArtPhoto
quelle
13
Das ist nicht richtig. Der Zeitstempel beim Laden des Modells wird für alle neuen Datensätze verwendet und nicht für die Zeit, zu der der Datensatz hinzugefügt wird.
SkyLeach
8
@SkyLeach Dieser Code ist korrekt. Sie können ihn hier testen: pastebin.com/VLyWktUn . datetime.datetime.utcnowscheint als Rückruf bezeichnet.
Bux
1
Ich habe diese Antwort verwendet, aber der Zeitstempel beim Laden des Modells wird für alle neuen Datensätze verwendet.
Scottydelta
105
@scottdelta: Stellen Sie sicher, dass Sie nicht verwenden default=datetime.datetime.utcnow(); Sie möchten die utcnowFunktion übergeben, nicht das Ergebnis der Auswertung bei Modullast.
Dysfunktion
Hat bei mir immer noch nicht funktioniert. Der Ansatz von Jeff-Widman hat für mich funktioniert:from sqlalchemy.sql import func; created_date = Column(DateTime(timezone=True), server_default=func.now()
Am
395

Berechnen Sie Zeitstempel in Ihrer Datenbank, nicht in Ihrem Client

Aus Gründen der Vernunft möchten Sie wahrscheinlich, dass alle datetimesDaten von Ihrem DB-Server und nicht vom Anwendungsserver berechnet werden. Das Berechnen des Zeitstempels in der Anwendung kann zu Problemen führen, da die Netzwerklatenz variabel ist, Clients eine geringfügig unterschiedliche Taktdrift aufweisen und verschiedene Programmiersprachen gelegentlich die Zeit geringfügig unterschiedlich berechnen.

Mit SQLAlchemy können Sie dies tun, indem Sie func.now()oder func.current_timestamp()(sie sind Aliase voneinander) übergeben, wodurch die DB angewiesen wird, den Zeitstempel selbst zu berechnen.

Verwenden Sie SQLALchemy's server_default

Darüber hinaus ist es für einen Standard, bei dem Sie der Datenbank bereits anweisen, den Wert zu berechnen, im Allgemeinen besser, ihn server_defaultanstelle von zu verwenden default. Dies weist SQLAlchemy an, den Standardwert als Teil der CREATE TABLEAnweisung zu übergeben.

Wenn Sie beispielsweise ein Ad-hoc-Skript für diese Tabelle schreiben, server_defaultbedeutet dies, dass Sie sich nicht um das manuelle Hinzufügen eines Zeitstempelaufrufs zu Ihrem Skript kümmern müssen. Die Datenbank legt dies automatisch fest.

SQLAlchemy verstehen onupdate/server_onupdate

SQLAlchemy unterstützt auch onupdate, dass bei jeder Aktualisierung der Zeile ein neuer Zeitstempel eingefügt wird. Auch hier ist es am besten, die DB anzuweisen, den Zeitstempel selbst zu berechnen:

from sqlalchemy.sql import func

time_created = Column(DateTime(timezone=True), server_default=func.now())
time_updated = Column(DateTime(timezone=True), onupdate=func.now())

Es gibt einen server_onupdateParameter, der jedoch im Gegensatz server_defaultdazu nichts Serverseitiges festlegt. Es teilt SQLalchemy lediglich mit, dass Ihre Datenbank die Spalte ändert, wenn eine Aktualisierung erfolgt (möglicherweise haben Sie einen Auslöser für die Spalte erstellt ), sodass SQLAlchemy nach dem Rückgabewert fragt, damit das entsprechende Objekt aktualisiert werden kann.

Ein weiteres mögliches Problem:

Es könnte Sie überraschen, dass alle Änderungen innerhalb einer einzelnen Transaktion denselben Zeitstempel haben. Dies liegt daran, dass der SQL-Standard angibt, dass CURRENT_TIMESTAMPWerte basierend auf dem Start der Transaktion zurückgegeben werden.

PostgreSQL bietet den Nicht-SQL-Standard statement_timestamp()und clock_timestamp()das tut Änderung innerhalb einer Transaktion. Dokumente hier: https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT

UTC-Zeitstempel

Wenn Sie UTC-Zeitstempel verwenden möchten, finden Sie func.utcnow()in der SQLAlchemy-Dokumentation einen Implementierungsstub für . Sie müssen jedoch selbst geeignete treiberspezifische Funktionen bereitstellen.

Jeff Widman
quelle
1
Ich habe gesehen, dass es jetzt eine Möglichkeit gibt, Postgres anzuweisen, die aktuelle Zeit zu verwenden, nicht nur die Startzeit der Transaktion ... es steht in den Postgres-Dokumenten
Jeff Widman
2
gibt es eine func.utcnow () oder so ähnlich?
Yerassyl
1
@ JeffWidman: Haben wir eine MySQL-Implementierung für utcnow? Ich in der Dokumentation nur mssql und postreg
JavaSa
1
Dies ist in der Tat eine großartige Antwort!
John Mutuma
3
server_default=func.now()arbeitete für mich, onupdate=func.now()tat es aber nicht. Versucht onupdate=datetime.datetime.utcnow(ohne Klammern), das hat auch nicht geholfen
Vikas Prasad
55

Sie können standardmäßig auch die integrierte Funktion sqlalchemy verwenden DateTime

from sqlalchemy.sql import func

DT = Column(DateTime(timezone=True), default=func.now())
QWERTY
quelle
9
Sie können server_defaultstattdessen auch einen defaultWert verwenden, der von der Datenbank selbst verarbeitet wird.
Rgtk
2
Wird func.now () nicht ausgeführt, wenn der Deskriptor definiert ist, sodass alle Modelle / Kinder (wenn dies eine Basisklasse ist) denselben DT-Wert haben. Durch Übergabe einer Funktionsreferenz wie 'datetime.datetime.utcnow' wird diese für jede separat ausgeführt. Dies ist entscheidend für "erstellte" oder "aktualisierte" Eigenschaften.
Metalstorm
10
@Metalstorm Nein, das ist richtig. sqlalchemy.sql.func ist ein Sonderfall, der eine Instanz von sqlalchemy.sql.functions.now zurückgibt, nicht die aktuelle Zeit. docs.sqlalchemy.org/en/latest/core/…
Kris Hardy
Was macht timezone=Truedas
ChaimG
1
@self, Aus der Dokumentation : "Wenn True und vom Backend unterstützt, wird 'TIMESTAMP WITH TIMEZONE' erzeugt. Für Backends, die keine
zeitzonenabhängigen
7

Sie möchten wahrscheinlich verwenden, onupdate=datetime.nowdamit UPDATEs auch das last_updatedFeld ändern .

SQLAlchemy hat zwei Standardeinstellungen für von Python ausgeführte Funktionen.

  • default Setzt den Wert auf INSERT nur einmal
  • onupdateSetzt den Wert auch bei UPDATE auf das aufrufbare Ergebnis.
user3389572
quelle
Ich weiß nicht warum, aber aus irgendeinem Grund onupdatetut es nichts für mich.
Vikas Prasad
1
Ich habe es endlich auf Postgres db zum Laufen gebracht: created_at = db.Column(db.DateTime, server_default=UtcNow())und updated_at = db.Column(db.DateTime, server_default=UtcNow(), onupdate=UtcNow())wo UtcNowist eine Klasse wie class UtcNow(expression.FunctionElement): type = DateTime()und wir haben@compiles(UtcNow, 'postgresql') def pg_utc_now(element, compiler, **kw): return "TIMEZONE('utc', CURRENT_TIMESTAMP)"
Vikas Prasad
6

Der defaultSchlüsselwortparameter sollte dem Column-Objekt zugewiesen werden.

Beispiel:

Column(u'timestamp', TIMESTAMP(timezone=True), primary_key=False, nullable=False, default=time_now),

Der Standardwert kann ein aufrufbarer Wert sein, den ich hier wie folgt definiert habe.

from pytz import timezone
from datetime import datetime

UTC = timezone('UTC')

def time_now():
    return datetime.now(UTC)
Keith
quelle
Woher kommt time_nowdas?
Kay - SE ist böse
Danke dir! Ich nahm an, dass es eine eingebaute Funktion mit diesem Namen gibt, die ich irgendwie übersehen habe.
Kay - SE ist böse
oderdatetime.datetime.utcnow
Serkef
1

Gemäß der PostgreSQL-Dokumentation https://www.postgresql.org/docs/9.6/static/functions-datetime.html

now, CURRENT_TIMESTAMP, LOCALTIMESTAMP return the time of transaction.

Dies wird als Merkmal angesehen: Es ist beabsichtigt, einer einzelnen Transaktion eine konsistente Vorstellung von der "aktuellen" Zeit zu ermöglichen, so dass mehrere Änderungen innerhalb derselben Transaktion denselben Zeitstempel tragen.

Möglicherweise möchten Sie den Anweisungstimestamp oder den Uhrzeitstempel verwenden, wenn Sie keinen Transaktionszeitstempel möchten.

statement_timestamp ()

Gibt die Startzeit der aktuellen Anweisung zurück (genauer gesagt den Zeitpunkt des Empfangs der letzten Befehlsnachricht vom Client). Anweisung_Zeitstempel

clock_timestamp ()

Gibt die aktuelle Uhrzeit zurück und daher ändert sich ihr Wert auch innerhalb eines einzelnen SQL-Befehls.

abhi shukla
quelle