SQLAlchemy default DateTime

175

Este es mi modelo declarativo:

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)

Sin embargo, cuando intento importar este módulo, aparece este error:

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'

Si uso un tipo entero, puedo establecer un valor predeterminado. ¿Que esta pasando?

Brandon O'Rourke
fuente
1
Esto no debería ser usado. utcnow es una marca de tiempo ingenua en la zona horaria UTC, sin embargo, es probable que las marcas de hora ingenuas se interpreten en la zona horaria local.
Antti Haapala
1
Sé que esta pregunta se hizo hace mucho tiempo, pero creo que su respuesta debería cambiarse por la que proporcionó @Jeff Widman, ya que la otra utilizará la fecha y hora de "compilación" cuando se define la clase de tabla versus cuando se crea el registro. Si está AFK, al menos este comentario proporcionará una advertencia para que otros verifiquen cuidadosamente los comentarios de cada pregunta.
David

Respuestas:

186

DateTimeno tiene una clave predeterminada como entrada. La tecla predeterminada debe ser una entrada a la Columnfunción. Prueba esto:

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
fuente
13
Esto no esta bien. La marca de tiempo en la carga del modelo se usará para todos los registros nuevos en lugar de la hora en que se agrega el registro.
SkyLeach
8
@SkyLeach Este código es correcto, puede probarlo aquí: pastebin.com/VLyWktUn . datetime.datetime.utcnowparece llamado como devolución de llamada.
bux
1
Utilicé esta respuesta, pero la marca de tiempo en la carga del modelo se está utilizando para todos los registros nuevos.
scottydelta
105
@scottdelta: asegúrese de no estar usando default=datetime.datetime.utcnow(); desea pasar la utcnowfunción, no el resultado de evaluarla en la carga del módulo.
disfunción
Todavía no funcionó para mí. El enfoque de Jeff-Widman trabajó para mí:from sqlalchemy.sql import func; created_date = Column(DateTime(timezone=True), server_default=func.now()
tharndt
396

Calcule las marcas de tiempo dentro de su base de datos, no su cliente

Para la cordura, es probable que desee tener todo datetimescalculado por su servidor de base de datos, en lugar de por el servidor de aplicaciones. Calcular la marca de tiempo en la aplicación puede ocasionar problemas porque la latencia de la red es variable, los clientes experimentan una deriva del reloj ligeramente diferente, y diferentes lenguajes de programación ocasionalmente calculan el tiempo de manera ligeramente diferente.

SQLAlchemy le permite hacer esto pasando func.now()o func.current_timestamp()(son alias entre sí) que le dice a la base de datos que calcule la marca de tiempo.

Use SQLALchemy's server_default

Además, para un valor predeterminado en el que ya le está diciendo a la base de datos que calcule el valor, generalmente es mejor usarlo en server_defaultlugar de hacerlo default. Esto le dice a SQLAlchemy que pase el valor predeterminado como parte de la CREATE TABLEdeclaración.

Por ejemplo, si escribe un script ad hoc en esta tabla, usar server_defaultsignifica que no tendrá que preocuparse por agregar manualmente una llamada de marca de tiempo a su script; la base de datos lo configurará automáticamente.

Comprensión de SQLAlchemy's onupdate/server_onupdate

SQLAlchemy también soporta onupdatede manera que en cualquier momento se actualiza la fila se inserta un nuevo orden de llegada. Nuevamente, es mejor decirle a la base de datos que calcule la marca de tiempo en sí:

from sqlalchemy.sql import func

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

Hay un server_onupdateparámetro, pero a diferencia server_default, en realidad no establece nada en el lado del servidor. Simplemente le dice a SQLalchemy que su base de datos cambiará la columna cuando ocurra una actualización (quizás haya creado un disparador en la columna ), por lo que SQLAlchemy le pedirá el valor de retorno para que pueda actualizar el objeto correspondiente.

Otro potencial problema:

Es posible que se sorprenda al notar que si realiza varios cambios dentro de una sola transacción, todos tienen la misma marca de tiempo. Esto se debe a que el estándar SQL especifica que CURRENT_TIMESTAMPdevuelve valores basados ​​en el inicio de la transacción.

PostgreSQL ofrece la no-estándar SQL statement_timestamp()y clock_timestamp()el que hacer el cambio dentro de una transacción. Documentos aquí: https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT

Marca de tiempo UTC

Si desea utilizar las marcas de tiempo UTC, un cabo de aplicación para func.utcnow()se proporciona en la documentación SQLAlchemy . Sin embargo, debe proporcionar funciones específicas del controlador apropiadas por su cuenta.

Jeff Widman
fuente
1
Vi que ahora hay una manera de decirle a Postgres que use la hora actual, no solo la hora de inicio de la transacción ... está en los documentos de Postgres
Jeff Widman
2
¿hay un func.utcnow () o algo así?
yerassyl
1
@JeffWidman: ¿Tenemos una implementación de mysql para utcnow? Yo en la documentación solo mssql y postreg
JavaSa
1
¡Esto, de hecho, es una gran respuesta!
John Mutuma
3
server_default=func.now()funcionó para mí, pero onupdate=func.now()no lo hizo. Probado onupdate=datetime.datetime.utcnow(sin corchetes), eso tampoco ayudó
Vikas Prasad
55

También puede usar la función incorporada sqlalchemy por defecto DateTime

from sqlalchemy.sql import func

DT = Column(DateTime(timezone=True), default=func.now())
QWERTY
fuente
9
También puede usar en server_defaultlugar de defaultlo que el valor lo manejará la base de datos.
rgtk
2
No se ejecuta func.now () cuando se define el descriptor, por lo que todos los modelos / hijos (si se trata de una clase base) tendrán el mismo valor de DT. Al pasar una referencia de función como 'datetime.datetime.utcnow' se ejecuta por separado para cada uno. Esto es crucial para las propiedades 'creadas' o 'actualizadas'.
Tormenta de metales
10
@Metalstorm No, esto es correcto. sqlalchemy.sql.func es un caso especial que devuelve una instancia de sqlalchemy.sql.functions.now, no la hora actual. docs.sqlalchemy.org/en/latest/core/…
Kris Hardy
¿Qué timezone=Truehacer?
ChaimG
1
@self, De la documentación : "Si es Verdadero, y respaldado por el back-end, producirá 'TIMESTAMP WITH TIMEZONE'. Para los backends que no admiten marcas de tiempo con
reconocimiento de
7

Es probable que desee usar onupdate=datetime.nowpara que las ACTUALIZACIONES también cambien el last_updatedcampo.

SQLAlchemy tiene dos valores predeterminados para las funciones ejecutadas por Python.

  • default establece el valor en INSERT, solo una vez
  • onupdatetambién establece el valor en el resultado invocable en UPDATE.
usuario3389572
fuente
No sé por qué, pero por alguna razón onupdateno hace nada por mí.
Vikas Prasad
1
Finalmente lo conseguí trabajando en Postgres db haciendo esto: created_at = db.Column(db.DateTime, server_default=UtcNow())y updated_at = db.Column(db.DateTime, server_default=UtcNow(), onupdate=UtcNow())dónde UtcNowes una clase como class UtcNow(expression.FunctionElement): type = DateTime()y tenemos@compiles(UtcNow, 'postgresql') def pg_utc_now(element, compiler, **kw): return "TIMEZONE('utc', CURRENT_TIMESTAMP)"
Vikas Prasad
6

El defaultparámetro de palabra clave debe darse al objeto Column.

Ejemplo:

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

El valor predeterminado puede ser invocable, que aquí definí como el siguiente.

from pytz import timezone
from datetime import datetime

UTC = timezone('UTC')

def time_now():
    return datetime.now(UTC)
Keith
fuente
De donde time_nowviene
kay - SE es malvado
¡Gracias! Supuse que había una función incorporada con ese nombre que de alguna manera pasé por alto.
kay - SE es malvado
odatetime.datetime.utcnow
serkef
1

Según la documentación de PostgreSQL, https://www.postgresql.org/docs/9.6/static/functions-datetime.html

now, CURRENT_TIMESTAMP, LOCALTIMESTAMP return the time of transaction.

Esto se considera una característica: la intención es permitir que una sola transacción tenga una noción coherente de la hora "actual", de modo que múltiples modificaciones dentro de la misma transacción tengan la misma marca de tiempo.

Es posible que desee utilizar la declaración de fecha y hora o la hora de reloj si no desea la marca de tiempo de la transacción.

Statement_timestamp ()

devuelve la hora de inicio de la declaración actual (más específicamente, la hora de recepción del último mensaje de comando del cliente). declaración_mpresa

clock_timestamp ()

devuelve la hora actual real y, por lo tanto, su valor cambia incluso dentro de un solo comando SQL.

abhi shukla
fuente