Problemas de fecha y hora de Django (predeterminado = datetime.now ())

283

Tengo el siguiente modelo de base de datos:

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)

Agrego una nueva instancia usando lo siguiente:

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

Mi problema: todos los registros en la base de datos tienen el mismo valor en el campo de fecha, que es la fecha del primer pago. Después de que el servidor se reinicia, un registro tiene la nueva fecha y los otros registros tienen el mismo que el primero. Parece que algunos datos están almacenados en caché, pero no puedo encontrar dónde.

base de datos: mysql 5.1.25

django v1.1.1

Shamanu4
fuente
1
¿No es posible usar una función como esta de forma predeterminada ?: default=datetime.now- nota, sin llamar como en now()No es el estándar para DateTimeField, pero ... práctico en cualquier caso.
viridis

Respuestas:

597

parece que datetime.now()se está evaluando cuando se define el modelo, y no cada vez que agrega un registro.

Django tiene una función para lograr lo que está intentando hacer:

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

o

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

La diferencia entre el segundo ejemplo y lo que tiene actualmente es la falta de paréntesis. Al pasar datetime.nowsin paréntesis, pasa la función real, que se llamará cada vez que se agregue un registro. Si lo pasa datetime.now(), solo está evaluando la función y pasándole el valor de retorno.

Más información está disponible en la referencia de campo modelo de Django

Carson Myers
fuente
206
nota importante : el uso de auto_now_add hace que el campo no sea editable en el administrador
Jiaaro
77
Gran respuesta, gracias. ¿Hay alguna manera de expresar una expresión más compleja con datetime.now como valor predeterminado? por ejemplo, ahora + 30 días (lo siguiente no funciona) expiry = models.DateTimeField(default=datetime.now + timedelta(days=30))
michela
26
@michela datetime.now es una función, y está intentando agregar un timedelta a una función. Tendrá que definir su propia devolución de llamada para establecer el valor predeterminado, como def now_plus_30(): return datetime.now() + timedelta(days = 30), y luego usarmodels.DateTimeField(default=now_plus_30)
Carson Myers
55
O, por supuesto, puede hacerlodefault=lambda: datetime.now()+timedelta(days=30)
Michael Mior,
34
TENGA EN CUENTA que usar auto_now_add NO es lo mismo que usar un valor predeterminado porque el campo SIEMPRE será igual a ahora (no editable). Sé que esto ya se dijo, pero no es solo un metter de la página 'admin'
VAShhh
115

En lugar de usar datetime.now, realmente deberías estar usandofrom django.utils.timezone import now

Referencia:

así que ve por algo como esto:

from django.utils.timezone import now


created_date = models.DateTimeField(default=now, editable=False)
andilabs
fuente
13
Esta es una nota muy importante! si usa datetime.now puede hacer una fecha y hora local que no sea consciente de la zona horaria. Si más tarde lo compara con un campo que se marcó con la fecha auto_now_add(que tiene en cuenta la zona horaria), puede obtener cálculos incorrectos debido a las diferencias de zona horaria erróneas.
Iftah
1
Esto definitivamente es muy importante, pero realmente no responde la pregunta por sí mismo.
Czechnology
1
definitivamente mejor manera
Carmine Tambascia
28

De la documentación en el campo predeterminado del modelo django:

El valor predeterminado para el campo. Esto puede ser un valor o un objeto invocable. Si se puede llamar, se llamará cada vez que se cree un nuevo objeto.

Por lo tanto, lo siguiente debería funcionar:

date = models.DateTimeField(default=datetime.now,blank=True)
mykhal
fuente
1
Esto solo es posible en django 1.8+
David Schumann
@DavidNathan ¿por qué es eso? Creo que ambas opciones han existido desde 1.4 o antes, incluido el uso de un invocable para default( django-chinese-docs-14.readthedocs.io/en/latest/ref/models/… )
Mark
16

David tenía la respuesta correcta. El paréntesis () hace que se llame al timezone.now () invocable cada vez que se evalúa el modelo. Si elimina el () de timezone.now () (o datetime.now (), si usa el ingenuo objeto datetime) para que sea así:

default=timezone.now

Entonces funcionará como espera: los
nuevos objetos recibirán la fecha actual cuando se creen, pero la fecha no se anulará cada vez que administre.py makemigrations / migrate.

Acabo de encontrar esto. Muchas gracias a David.

MicahT
fuente
10

El datetime.now()se evalúa cuando se crea la clase, no cuando se agrega un nuevo registro a la base de datos.

Para lograr lo que desea, defina este campo como:

date = models.DateTimeField(auto_now_add=True)

De esta manera, el datecampo se establecerá en la fecha actual para cada nuevo registro.

Bartosz
fuente
6

La respuesta a esta pregunta es realmente incorrecta.

Rellenar automáticamente el valor (auto_now / auto_now_add no es lo mismo que el predeterminado). El valor predeterminado será en realidad lo que el usuario ve si es un objeto nuevo. Lo que normalmente hago es:

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

Asegúrese de que, si está tratando de representar esto en una página de administración, lo enumere como 'solo lectura' y haga referencia al nombre del campo

read_only = 'date'

Nuevamente, hago esto ya que mi valor predeterminado no suele ser editable, y las páginas de administración ignoran los no editables a menos que se especifique lo contrario. Sin embargo, hay una diferencia entre establecer un valor predeterminado e implementar auto_add, que es clave aquí. ¡Pruébalo!

Andrew Harris
fuente
6

datetime.now()se evalúa una vez, cuando se instancia su clase. Intente eliminar el paréntesis para que la función datetime.nowse devuelva y luego se evalúe. Tuve el mismo problema con el establecimiento de valores predeterminados para mis DateTimeFieldcorreos electrónicos y escribí mi solución aquí .

David
fuente
5

De la referencia del lenguaje Python, en Definiciones de funciones :

Los valores de los parámetros predeterminados se evalúan cuando se ejecuta la definición de la función. Esto significa que la expresión se evalúa una vez, cuando se define la función, y que se utiliza el mismo valor "precalculado" para cada llamada.

Afortunadamente, Django tiene una manera de hacer lo que quiere, si usa el auto_nowargumento para DateTimeField:

date = models.DateTimeField(auto_now=True)

Vea los documentos de Django para DateTimeField .

ars
fuente
0

En Django 3.0 auto_now_addparece funcionar conauto_now

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

Bosco Oludhe
fuente