DateTime predefinito di SQLAlchemy


174

Questo è il mio modello dichiarativo:

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)

Tuttavia, quando provo a importare questo modulo, ottengo questo errore:

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'

Se utilizzo un tipo intero, posso impostare un valore predefinito. Cosa sta succedendo?


1
Questo non dovrebbe essere usato. utcnow è un timestamp ingenuo nel fuso orario UTC, tuttavia è probabile che i timestamp ingenui vengano interpretati nel fuso orario locale.
Antti Haapala,

1
So che questa domanda è stata posta molto tempo fa, ma penso che la tua risposta dovrebbe essere cambiata in quella che @Jeff Widman ha fornito, poiché l'altro utilizzerà il "datile" time datetime quando viene definita la classe table rispetto a quando viene creato il record. Se sei AFK, almeno questo commento fornirà un avvertimento per gli altri per controllare attentamente i commenti per ogni domanda.
David,

Risposte:


186

DateTimenon ha una chiave predefinita come input. La chiave predefinita dovrebbe essere un input per la Columnfunzione. Prova questo:

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)

13
Questo non è giusto Il timestamp al caricamento del modello verrà utilizzato per tutti i nuovi record anziché per il momento in cui il record viene aggiunto.
SkyLeach

8
@SkyLeach Questo codice è corretto, puoi provarlo qui: pastebin.com/VLyWktUn . datetime.datetime.utcnowsembra chiamato come callback.
Bux

1
Ho usato questa risposta ma il timestamp al caricamento del modello viene utilizzato per tutti i nuovi record.
scottydelta,

105
@scottdelta: assicurati di non utilizzare default=datetime.datetime.utcnow(); si desidera passare la utcnowfunzione, non il risultato della valutazione al caricamento del modulo.
disfunzione

Non ha ancora funzionato per me. L'approccio di jeff-widman ha funzionato per me:from sqlalchemy.sql import func; created_date = Column(DateTime(timezone=True), server_default=func.now()
tharndt,

396

Calcola i timestamp all'interno del tuo DB, non del tuo client

Per sanità di mente, probabilmente vorrai aver datetimescalcolato tutto dal tuo server DB, piuttosto che dal server delle applicazioni. Il calcolo del timestamp nell'applicazione può causare problemi poiché la latenza della rete è variabile, i client riscontrano una deriva dell'orologio leggermente diversa e diversi linguaggi di programmazione occasionalmente calcolano il tempo in modo leggermente diverso.

SQLAlchemy ti permette di farlo passando func.now()o func.current_timestamp()(sono alias l'uno dell'altro) che dice al DB di calcolare il timestamp stesso.

Usa SQLALchemy's server_default

Inoltre, per impostazione predefinita in cui si sta già dicendo al DB di calcolare il valore, è generalmente meglio usare server_defaultinvece di default. Questo dice a SQLAlchemy di passare il valore predefinito come parte dell'istruzione CREATE TABLE.

Ad esempio, se si scrive uno script ad hoc su questa tabella, utilizzando server_default significa che non sarà necessario preoccuparsi di aggiungere manualmente una chiamata timestamp allo script: il database lo imposterà automaticamente.

Comprensione di SQLAlchemy onupdate/server_onupdate

SQLAlchemy supporta inoltre in onupdatemodo che ogni volta che la riga viene aggiornata inserisca un nuovo timestamp. Ancora una volta, è meglio dire al DB di calcolare il timestamp stesso:

from sqlalchemy.sql import func

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

C'è un server_onupdateparametro, ma a differenza server_default, in realtà non imposta nulla sul lato server. Dice solo a SQLalchemy che il tuo database cambierà la colonna quando si verifica un aggiornamento (forse hai creato un trigger sulla colonna ), quindi SQLAlchemy chiederà il valore restituito in modo che possa aggiornare l'oggetto corrispondente.

Un altro potenziale gotcha:

Potresti essere sorpreso di notare che se apporti una serie di modifiche all'interno di una singola transazione, tutte hanno lo stesso timestamp. Questo perché lo standard SQL lo specificaCURRENT_TIMESTAMP restituisce valori basati sull'inizio della transazione.

PostgreSQL fornisce la non-SQL standard statement_timestamp()e clock_timestamp()che fare il cambiamento all'interno di una transazione. Documenti qui: https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT

Timestamp UTC

Se si desidera utilizzare i timestamp UTC, func.utcnow()nella documentazione di SQLAlchemy viene fornito uno stub di implementazione . Tuttavia, è necessario fornire autonomamente funzioni appropriate specifiche del driver.


1
Ho visto che ora c'è un modo per dire a Postgres di usare l'ora corrente, non solo l'ora di inizio della transazione ... è nei documenti di Postgres
Jeff Widman,

2
c'è un func.utcnow () o qualcosa del genere?
yerassyl,

1
@JeffWidman: abbiamo un'implementazione mysql per utcnow? Nella documentazione solo mssql e postreg
JavaSa

1
Questa, in effetti, è un'ottima risposta!
John Mutuma,

3
server_default=func.now()ha funzionato per me, ma onupdate=func.now()non l'ha fatto. Ho provato onupdate=datetime.datetime.utcnow(senza parentesi), anche quello non ha aiutato
Vikas Prasad il

55

Puoi anche usare la funzione built-in sqlalchemy per impostazione predefinita DateTime

from sqlalchemy.sql import func

DT = Column(DateTime(timezone=True), default=func.now())

9
È inoltre possibile utilizzare server_defaultinvece di defaultcosì valore verrà gestito dal database stesso.
rgtk,

2
Func.now () non viene eseguito quando viene definito il descrittore, quindi tutti i modelli / figli (se questa è una classe base) avranno lo stesso valore DT. Passando un riferimento di funzione come 'datetime.datetime.utcnow' viene eseguito separatamente per ciascuno. Questo è fondamentale per le proprietà "create" o "aggiornate".
Metalstorm,

10
@Metalstorm No, questo è corretto. sqlalchemy.sql.func è un caso speciale che restituisce un'istanza sqlalchemy.sql.functions.now, non l'ora corrente. docs.sqlalchemy.org/en/latest/core/…
Kris Hardy

Cosa fa timezone=True?
ChaimG,

1
@self, From documentazione : "Se True, e supportato dal back-end, produrrà 'TIMESTAMP WITH TIMEZONE'. Per i backend che non supportano timestamp consapevoli del fuso orario, non ha alcun effetto .
ChaimG

7

Probabilmente vuoi usarlo in onupdate=datetime.nowmodo che anche gli AGGIORNAMENTI cambino il last_updatedcampo.

SQLAlchemy ha due valori predefiniti per le funzioni eseguite da Python.

  • default imposta il valore su INSERT, solo una volta
  • onupdateimposta il valore sul risultato richiamabile anche su UPDATE.

Non so perché, ma per qualche motivo onupdatenon fa nulla per me.
Vikas Prasad,

1
Alla fine l'ho fatto funzionare su Postgres db facendo questo: created_at = db.Column(db.DateTime, server_default=UtcNow())e updated_at = db.Column(db.DateTime, server_default=UtcNow(), onupdate=UtcNow())dov'è UtcNowuna classe come class UtcNow(expression.FunctionElement): type = DateTime()e abbiamo@compiles(UtcNow, 'postgresql') def pg_utc_now(element, compiler, **kw): return "TIMEZONE('utc', CURRENT_TIMESTAMP)"
Vikas Prasad,

6

Il defaultparametro della parola chiave deve essere assegnato all'oggetto Colonna.

Esempio:

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

Il valore predefinito può essere richiamabile, che qui ho definito come il seguente.

from pytz import timezone
from datetime import datetime

UTC = timezone('UTC')

def time_now():
    return datetime.now(UTC)

Da dove time_nowviene
kay - SE è cattivo il

Grazie! Ho pensato che ci fosse una funzione integrata con quel nome che in qualche modo ho trascurato.
kay - SE è cattivo il

oppuredatetime.datetime.utcnow
serkef,

1

Come da documentazione PostgreSQL, https://www.postgresql.org/docs/9.6/static/functions-datetime.html

now, CURRENT_TIMESTAMP, LOCALTIMESTAMP return the time of transaction.

Questa è considerata una caratteristica: l'intenzione è quella di consentire a una singola transazione di avere una nozione coerente del tempo "corrente", in modo che più modifiche all'interno della stessa transazione rechino lo stesso timestamp.

Potresti voler usare statement_timestamp o clock_timestamp se non vuoi il timestamp della transazione.

statement_timestamp ()

restituisce l'ora di inizio dell'istruzione corrente (in particolare, l'ora di ricezione dell'ultimo messaggio di comando dal client). statement_timestamp

clock_timestamp ()

restituisce l'ora corrente attuale e quindi il suo valore cambia anche all'interno di un singolo comando SQL.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.