Problemas de data e hora do Django (padrão = datetime.now ())

283

Eu tenho o modelo db abaixo:

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)

Eu adiciono uma nova instância usando o abaixo:

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

Meu problema: todos os registros no banco de dados têm o mesmo valor no campo data, que é a data do primeiro pagamento. Depois que o servidor reiniciar, um registro terá a nova data e os outros registros terão a mesma do primeiro. Parece que alguns dados estão em cache, mas não consigo encontrar onde.

banco de dados: mysql 5.1.25

django v1.1.1

Shamanu4
fonte
1
Não é possível usar como padrão uma função como essa ?: default=datetime.now- note, sem chamar como now()Não é o padrão para DateTimeField, mas ... útil em qualquer caso.
Viridis

Respostas:

597

parece que datetime.now()está sendo avaliado quando o modelo é definido, e não sempre que você adiciona um registro.

O Django possui um recurso para realizar o que você já está tentando fazer:

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

ou

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

A diferença entre o segundo exemplo e o que você tem atualmente é a falta de parênteses. Ao passar datetime.nowsem parênteses, você está passando a função real, que será chamada sempre que um registro for adicionado. Se você a passar datetime.now(), estará apenas avaliando a função e passando o valor de retorno.

Mais informações estão disponíveis na referência de campo do modelo do Django

Carson Myers
fonte
206
nota importante : usar auto_now_add torna o campo não editável no admin
Jiaaro
7
Ótima resposta, obrigado. Existe uma maneira de expressar uma expressão mais complexa com datetime.now como padrão? por exemplo, agora + 30 dias (o seguinte não funciona) expiry = models.DateTimeField(default=datetime.now + timedelta(days=30))
michela 5/05
26
@michela datetime.now é uma função e você está tentando adicionar um timedelta a uma função. Você vai ter que definir o seu próprio retorno de chamada para definir o valor padrão, como def now_plus_30(): return datetime.now() + timedelta(days = 30), e depois usarmodels.DateTimeField(default=now_plus_30)
Carson Myers
55
Ou você pode, claro, fazerdefault=lambda: datetime.now()+timedelta(days=30)
Michael Mior
34
CIENTE que o uso auto_now_add é não o mesmo que usar um padrão, porque o campo será sempre igual a agora (não editável) .. Eu sei que isso já foi dito, mas não é apenas um metter da página 'admin'
VAShhh
115

Em vez de usar, datetime.nowvocê deve realmente usarfrom django.utils.timezone import now

Referência:

então escolha algo assim:

from django.utils.timezone import now


created_date = models.DateTimeField(default=now, editable=False)
andilabs
fonte
13
Esta é uma nota muito importante! se você usar datetime.now, poderá encontrar um datetime local que não esteja ciente do fuso horário. Se você compará-lo posteriormente com um campo com registro de data e hora usando auto_now_add(que reconhece o fuso horário), poderá obter cálculos errados devido a diferenças errôneas de fuso horário.
Iftah
1
Definitivamente, isso é muito importante, mas realmente não responde à pergunta por si só.
Czechnology
1
definitivamente melhor maneira
Carmine Tambascia
28

Na documentação no campo padrão do modelo django:

O valor padrão para o campo. Pode ser um valor ou um objeto que pode ser chamado. Se for chamado, será chamado toda vez que um novo objeto for criado.

Portanto, o seguinte deve funcionar:

date = models.DateTimeField(default=datetime.now,blank=True)
mykhal
fonte
1
Isso só é possível no django 1.8+
David Schumann
@DavidNathan por que isso? Eu acho que ambas as opções têm sido em torno desde 1.4 ou antes, incluindo o uso de um exigível para default( django-chinese-docs-14.readthedocs.io/en/latest/ref/models/... )
Mark
16

David teve a resposta certa. O parêntese () faz com que o fuso horário chamado.now () seja chamado toda vez que o modelo for avaliado. Se você remover o () de timezone.now () (ou datetime.now (), se estiver usando o ingênuo objeto datetime) para torná-lo exatamente isso:

default=timezone.now

Depois, funcionará conforme o esperado:
novos objetos receberão a data atual quando eles forem criados, mas a data não será substituída toda vez que você gerenciar.py makemigrations / migrate.

Acabei de encontrar isso. Muito obrigado a David.

MicahT
fonte
10

O datetime.now()é avaliado quando a classe é criada, não quando um novo registro está sendo adicionado ao banco de dados.

Para alcançar o que você deseja, defina esse campo como:

date = models.DateTimeField(auto_now_add=True)

Dessa forma, o datecampo será definido para a data atual de cada novo registro.

Bartosz
fonte
6

A resposta para esta aqui está realmente errada.

O preenchimento automático do valor (auto_now / auto_now_add não é o mesmo padrão). O valor padrão será realmente o que o usuário vê se for um objeto novo. O que eu normalmente faço é:

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

Certifique-se de que, se você está tentando representar isso em uma página de administrador, liste-o como 'read_only' e faça referência ao nome do campo

read_only = 'date'

Novamente, faço isso porque meu valor padrão normalmente não é editável e as páginas de Admin ignoram os não editáveis, a menos que especificado de outra forma. Certamente, há uma diferença entre definir um valor padrão e implementar o auto_add, que é a chave aqui. Teste!

Andrew Harris
fonte
6

datetime.now()está sendo avaliado uma vez, quando sua turma é instanciada. Tente remover os parênteses para que a função datetime.nowseja retornada e depois avaliada. Eu tive o mesmo problema ao definir valores padrão para meus DateTimeFielde escrevi minha solução aqui .

David
fonte
5

Na referência da linguagem Python, em Definições de função :

Os valores padrão dos parâmetros são avaliados quando a definição da função é executada. Isso significa que a expressão é avaliada uma vez, quando a função é definida, e que o mesmo valor "pré-calculado" é usado para cada chamada.

Felizmente, o Django tem uma maneira de fazer o que você deseja, se você usar o auto_nowargumento para DateTimeField:

date = models.DateTimeField(auto_now=True)

Veja os documentos do Django para DateTimeField .

ars
fonte
0

No Django 3.0 auto_now_addparece funcionar comauto_now

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

Bosco Oludhe
fonte