SQLAlchemy DateTime padrão

174

Este é o meu 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)

No entanto, quando tento importar este módulo, recebo este erro:

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 eu usar um tipo Inteiro, posso definir um valor padrão. O que está acontecendo?

Brandon O'Rourke
fonte
1
Isso não deve ser usado. utcnow é um registro de data e hora ingênuo no fuso horário UTC; no entanto, é provável que os registros de data e hora ingênuos sejam interpretados no fuso horário local.
Antti Haapala 06/10
1
Sei que essa pergunta foi feita há muito tempo, mas acho que sua resposta deve ser alterada para a que @Jeff Widman forneceu, pois o outro usará o tempo de compilação "datetime" quando a classe da tabela for definida e quando o registro for criado. Se você é AFK, pelo menos esse comentário fornecerá um aviso para que outras pessoas verifiquem cuidadosamente os comentários de cada pergunta.
David

Respostas:

186

DateTimenão possui uma chave padrão como entrada. A tecla padrão deve ser uma entrada para a Columnfunção. Tente o seguinte:

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
fonte
13
Isso não está certo. O registro de data e hora no carregamento do modelo será usado para todos os novos registros, e não na hora em que o registro for adicionado.
SkyLeach 9/16
8
@SkyLeach Este código está correto, você pode testá-lo aqui: pastebin.com/VLyWktUn . datetime.datetime.utcnowparece chamado como retorno de chamada.
bux
1
Usei essa resposta, mas o carimbo de data e hora no carregamento do modelo está sendo usado para todos os novos registros.
precisa saber é o seguinte
105
@ scottdelta: verifique se você não está usando default=datetime.datetime.utcnow(); você deseja passar a utcnowfunção, não o resultado da avaliação na carga do módulo.
disfunção
Ainda não funcionou para mim. A abordagem de jeff-widman funcionou para mim:from sqlalchemy.sql import func; created_date = Column(DateTime(timezone=True), server_default=func.now()
tharndt 21/01
396

Calcular carimbos de data e hora no seu banco de dados, não no seu cliente

Por questões de sanidade, você provavelmente deseja ter todos datetimescalculados pelo seu servidor de banco de dados, e não pelo servidor de aplicativos. O cálculo do registro de data e hora no aplicativo pode causar problemas, porque a latência da rede é variável, os clientes experimentam um desvio do relógio ligeiramente diferente e diferentes linguagens de programação ocasionalmente calculam o tempo de maneira ligeiramente diferente.

SQLAlchemy permite que você faça isso passando func.now()ou func.current_timestamp()(eles são aliases um do outro) que informa ao banco de dados para calcular o próprio carimbo de data / hora.

Use SQLALchemy server_default

Além disso, para um padrão em que você já está dizendo ao banco de dados para calcular o valor, geralmente é melhor usá-lo em server_defaultvez de default. Isso informa ao SQLAlchemy para passar o valor padrão como parte da CREATE TABLEinstrução.

Por exemplo, se você escrever um script ad hoc nessa tabela, usar server_defaultsignifica que não precisará se preocupar em adicionar manualmente uma chamada de carimbo de data / hora ao seu script - o banco de dados o definirá automaticamente.

Noções básicas sobre SQLAlchemy onupdate/server_onupdate

O SQLAlchemy também suporta, onupdatepara que, sempre que a linha for atualizada, ele insira um novo carimbo de data / hora. Novamente, é melhor dizer ao banco de dados para calcular o próprio carimbo de data / hora:

from sqlalchemy.sql import func

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

Existe um server_onupdateparâmetro, mas, ao contrário server_default, ele não define nada no servidor. Apenas informa ao SQLalchemy que seu banco de dados alterará a coluna quando uma atualização acontecer (talvez você tenha criado um gatilho na coluna ); portanto, o SQLAlchemy solicitará o valor de retorno para que ele possa atualizar o objeto correspondente.

Uma outra pegada em potencial:

Você pode se surpreender ao perceber que, se você fizer várias alterações em uma única transação, todas elas terão o mesmo carimbo de data / hora. Isso ocorre porque o padrão SQL especifica que CURRENT_TIMESTAMPretorna valores com base no início da transação.

PostgreSQL fornece o não-SQL-padrão statement_timestamp()e clock_timestamp()que fazer a mudança dentro de uma transação. Documentos aqui: https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT

Registro de data e hora UTC

Se você deseja usar registros de data e hora UTC, func.utcnow()é fornecido um stub de implementação na documentação do SQLAlchemy . Você precisa fornecer as funções específicas do driver apropriadas por conta própria.

Jeff Widman
fonte
1
Eu vi agora há uma maneira de dizer Postgres usar o tempo atual, não apenas a hora de início transação ... é nos docs postgres
Jeff Widman
2
existe um func.utcnow () ou algo parecido?
precisa
1
@ JeffWidman: Nós temos uma implementação mysql para o utcnow? I na documentação apenas postreg mssql e
JavaSa
1
Esta, de fato, é uma ótima resposta!
John Mutuma
3
server_default=func.now()trabalhou para mim, mas onupdate=func.now()não. Tentei onupdate=datetime.datetime.utcnow(sem parênteses), que também não ajudou
Vikas Prasad
55

Você também pode usar a função interna sqlalchemy por padrão DateTime

from sqlalchemy.sql import func

DT = Column(DateTime(timezone=True), default=func.now())
qwerty
fonte
9
Você também pode usar, em server_defaultvez de default, o valor será tratado pelo próprio banco de dados.
Rgtk
2
O func.now () não é executado quando o descritor é definido, portanto, todos os modelos / filhos (se for uma classe base) terão o mesmo valor de DT. Ao passar uma referência de função como 'datetime.datetime.utcnow', ela é executada separadamente para cada uma. Isso é crucial para propriedades 'criadas' ou 'atualizadas'.
Metalstorm
10
@Metalstorm Não, isso está correto. sqlalchemy.sql.func é um caso especial que retorna uma instância sqlalchemy.sql.functions.now, não a hora atual. docs.sqlalchemy.org/en/latest/core/…
Kris Hardy
O que timezone=Truefaz?
ChaimG
1
@self, A partir da documentação : "Se True, e suportado pelo back-end, produzirá 'TIMESTAMP WITH TIMEZONE'. Para back-end que não suportam timestamps com reconhecimento de fuso horário, não tem efeito .
ChaimG
7

Você provavelmente deseja usar onupdate=datetime.nowpara que UPDATEs também alterem o last_updatedcampo.

SQLAlchemy possui dois padrões para funções executadas em python.

  • default define o valor em INSERT, apenas uma vez
  • onupdatedefine o valor para o resultado que pode ser chamado em UPDATE também.
user3389572
fonte
Não sei por que, mas, por algum motivo onupdate, não faz nada por mim.
Vikas Prasad
1
Eu finalmente tenho que trabalhar em Postgres db fazendo isso: created_at = db.Column(db.DateTime, server_default=UtcNow())e updated_at = db.Column(db.DateTime, server_default=UtcNow(), onupdate=UtcNow())onde UtcNowé uma classe como class UtcNow(expression.FunctionElement): type = DateTime()e nós temos@compiles(UtcNow, 'postgresql') def pg_utc_now(element, compiler, **kw): return "TIMEZONE('utc', CURRENT_TIMESTAMP)"
Vikas Prasad
6

O defaultparâmetro keyword deve ser fornecido ao objeto Column.

Exemplo:

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

O valor padrão pode ser exigível, que aqui defini da seguinte maneira.

from pytz import timezone
from datetime import datetime

UTC = timezone('UTC')

def time_now():
    return datetime.now(UTC)
Keith
fonte
De onde time_nowvem?
kay - SE is evil
Obrigado! Eu assumi que havia uma função interna com esse nome que de alguma forma eu esqueci.
kay - SE é mau
oudatetime.datetime.utcnow
serkef 24/06/19
1

De acordo com a documentação do PostgreSQL, https://www.postgresql.org/docs/9.6/static/functions-datetime.html

now, CURRENT_TIMESTAMP, LOCALTIMESTAMP return the time of transaction.

Isso é considerado um recurso: a intenção é permitir que uma única transação tenha uma noção consistente do horário "atual", de modo que várias modificações na mesma transação tenham o mesmo carimbo de hora.

Você pode usar statement_timestamp ou clock_timestamp se não desejar o carimbo de data / hora da transação.

statement_timestamp ()

retorna a hora de início da instrução atual (mais especificamente, a hora do recebimento da última mensagem de comando do cliente). statement_timestamp

clock_timestamp ()

retorna a hora atual real e, portanto, seu valor muda mesmo dentro de um único comando SQL.

abhi shukla
fonte