Problemi di datetime di Django (default = datetime.now ())


284

Ho il seguente modello db:

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)

Aggiungo una nuova istanza utilizzando il seguente:

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

Il mio problema: tutti i record nel database hanno lo stesso valore nel campo data, che è la data del primo pagamento. Dopo il riavvio del server, un record ha la nuova data e gli altri hanno lo stesso del primo. Sembra che alcuni dati siano memorizzati nella cache, ma non riesco a trovare dove.

database: mysql 5.1.25

django v1.1.1


1
Non è possibile default una funzione come questo ?: default=datetime.now- nota, senza chiamare come in now()Non lo standard per DateTimeField, ma ... a portata di mano anycase.
viridis,

Risposte:


597

sembra che datetime.now()venga valutato quando viene definito il modello e non ogni volta che aggiungi un record.

Django ha una funzione per realizzare ciò che stai già cercando di fare:

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

o

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

La differenza tra il secondo esempio e quello che hai attualmente è la mancanza di parentesi. Passando datetime.nowsenza le parentesi, si passa la funzione effettiva, che verrà chiamata ogni volta che viene aggiunto un record. Se lo passi datetime.now(), stai solo valutando la funzione e passando il valore restituito.

Maggiori informazioni sono disponibili nel riferimento sul campo del modello di Django


206
nota importante : l'uso di auto_now_add rende il campo non modificabile
nell'amministratore

7
Ottima risposta, grazie. Esiste un modo per esprimere un'espressione più complessa con datetime.now come predefinito? es. ora + 30 giorni (il seguente non funziona) expiry = models.DateTimeField(default=datetime.now + timedelta(days=30))
michela

26
@michela datetime.now è una funzione e stai cercando di aggiungere un timedelta a una funzione. Dovrai definire il tuo callback per impostare il valore predefinito, come def now_plus_30(): return datetime.now() + timedelta(days = 30)e quindi usaremodels.DateTimeField(default=now_plus_30)
Carson Myers

55
O ovviamente puoi farlodefault=lambda: datetime.now()+timedelta(days=30)
Michael Mior,

34
ESSERE CONSAPEVOLI che l'utilizzo di auto_now_add è NON lo stesso che utilizzare un default perché il campo sarà sempre uguale ad oggi (non modificabile) .. lo so che questo è stato già detto, ma non è solo una metter della pagina 'admin'
VAShhh

115

Invece di usare datetime.now, dovresti davvero usarefrom django.utils.timezone import now

Riferimento:

quindi vai per qualcosa del genere:

from django.utils.timezone import now


created_date = models.DateTimeField(default=now, editable=False)

13
Questa è una nota molto importante! se usi datetime.now puoi utilizzare un datetime locale che non è a conoscenza del fuso orario. Se in seguito lo si confronta con un campo che è stato marcato con il timestamp utilizzando auto_now_add(che è a conoscenza del fuso orario) è possibile ottenere calcoli errati a causa delle differenze di fuso orario errate.
Iftah,

1
Questo è sicuramente molto importante ma non risponde alla domanda da solo.
Cecologia

1
modo
decisamente

28

Dalla documentazione sul campo predefinito del modello django:

Il valore predefinito per il campo. Questo può essere un valore o un oggetto richiamabile. Se richiamabile, verrà chiamato ogni volta che viene creato un nuovo oggetto.

Pertanto dovrebbe funzionare:

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

1
Questo è possibile solo in Django 1.8+
David Schumann,

@DavidNathan perché è quello? Penso che entrambe le opzioni siano in circolazione dall'1.4 o prima, incluso l'uso di un callable per default( django-chinese-docs-14.readthedocs.io/en/latest/ref/models/… )
Mark

16

David aveva la risposta giusta. La parentesi () lo rende in modo tale che il callable timezone.now () venga chiamato ogni volta che viene valutato il modello. Se si rimuove () da timezone.now () (o datetime.now (), se si utilizza l'oggetto datetime naive) per renderlo proprio questo:

default=timezone.now

Quindi funzionerà come previsto: i
nuovi oggetti riceveranno la data corrente al momento della loro creazione, ma la data non verrà sovrascritta ogni volta che si gestisce.py makemigrations / migrate.

L'ho appena incontrato. Mille grazie a David.


10

Il datetime.now()viene valutata quando viene creata la classe, non quando viene aggiunto nuovo record al database.

Per ottenere ciò che desideri, definire questo campo come:

date = models.DateTimeField(auto_now_add=True)

In questo modo il datecampo verrà impostato alla data corrente per ogni nuovo record.


6

La risposta a questa è in realtà sbagliata.

Compilazione automatica del valore (auto_now / auto_now_add non è uguale al valore predefinito). Il valore predefinito sarà effettivamente ciò che l'utente vede se si tratta di un oggetto nuovo di zecca. Quello che faccio di solito è:

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

Assicurati, se stai cercando di rappresentarlo in una pagina di amministrazione, che lo elenchi come "read_only" e fai riferimento al nome del campo

read_only = 'date'

Ancora una volta, lo faccio perché il mio valore predefinito non è in genere modificabile e le pagine di amministrazione ignorano i non modificabili se non diversamente specificato. Esiste sicuramente una differenza tra l'impostazione di un valore predefinito e l'implementazione di auto_add, che è la chiave qui. Provalo!


6

datetime.now()viene valutato una volta, quando la tua classe viene istanziata. Prova a rimuovere la parentesi in modo che la funzione datetime.nowvenga restituita e POI valutata. Ho avuto lo stesso problema con l'impostazione dei valori predefiniti per i miei messaggi DateTimeFielde ho scritto la mia soluzione qui .


5

Dal riferimento al linguaggio Python, in Definizioni funzioni :

I valori dei parametri predefiniti vengono valutati quando viene eseguita la definizione della funzione. Ciò significa che l'espressione viene valutata una volta, quando viene definita la funzione, e che lo stesso valore "pre-calcolato" viene utilizzato per ogni chiamata.

Fortunatamente, Django ha un modo di fare quello che vuoi, se usi l' auto_nowargomento per DateTimeField:

date = models.DateTimeField(auto_now=True)

Consulta i documenti Django per DateTimeField .


0

In Django 3.0 auto_now_addsembra funzionareauto_now

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

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.