python - example - Django auto_now y auto_now_add
django insert date (12)
Para Django 1.1.
Tengo esto en mis modelos.
class User(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
Al actualizar una fila me sale:
[Sun Nov 15 02:18:12 2009] [error] /home/ptarjan/projects/twitter-meme/django/db/backends/mysql/base.py:84: Warning: Column ''created'' cannot be null
[Sun Nov 15 02:18:12 2009] [error] return self.cursor.execute(query, args)
La parte relevante de mi base de datos es:
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
¿Esto es motivo de preocupación?
Pregunta secundaria: en mi herramienta de administración, esos dos campos no aparecen. ¿Se espera eso?
¿Esto es motivo de preocupación?
No, Django lo agrega automáticamente al guardar los modelos, por lo que se espera.
Pregunta secundaria: en mi herramienta de administración, esos 2 campos no se muestran. ¿Se espera eso?
Dado que estos campos se agregan automáticamente, no se muestran.
Para agregar a lo anterior, como dijo synack, ha habido un debate en la lista de correo de django para eliminar esto, porque "no está bien diseñado" y es "un hack"
Escribir un guardado personalizado () en cada uno de mis modelos es mucho más doloroso que usar auto_now
Obviamente no tienes que escribirlo en todos los modelos. Puedes escribirlo en un modelo y heredar otros de él.
Pero, como auto_add
y auto_now_add
están ahí, los usaría en lugar de intentar escribir un método yo mismo.
Aquí está la respuesta si está usando el sur y desea establecer de manera predeterminada la fecha en que agrega el campo a la base de datos:
Elija la opción 2 y luego: datetime.datetime.now ()
Se ve como esto:
$ ./manage.py schemamigration myapp --auto
? The field ''User.created_date'' does not have a default specified, yet is NOT NULL.
? Since you are adding this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now, and add a default to the field in models.py
? 2. Specify a one-off value to use for existing columns now
? Please select a choice: 2
? Please enter Python code for your one-off default value.
? The datetime module is available, so you can do e.g. datetime.date.today()
>>> datetime.datetime.now()
+ Added field created_date on myapp.User
Bah ... No hay suficiente reputación como para comentar ... Pero quería señalar que la opinión expresada en la respuesta aceptada está algo desfasada. De acuerdo con las discusiones más recientes (errores #7634 y #12785 ), auto_now y auto_now_add no van a ninguna parte, e incluso si vas a la discusión original , encontrarás fuertes argumentos en contra del RY (como en DRY) en el guardado personalizado metodos
Se ha ofrecido una mejor solución (tipos de campos personalizados), pero no obtuvo el impulso suficiente para convertirse en django. Puedes escribir el tuyo en tres líneas (es la sugerencia de Jacob Kaplan-Moss).
class AutoDateTimeField(models.DateTimeField):
def pre_save(self, model_instance, add):
return datetime.datetime.now()
#usage
created_at = models.DateField(default=timezone.now)
updated_at = models.AutoDateTimeField(default=timezone.now)
Basado en lo que he leído y mi experiencia con Django hasta ahora, auto_now_add tiene errores. Estoy de acuerdo con jthanism --- anula el método de guardado normal, está limpio y sabes lo que está pasando. Ahora, para que se seque, crea un modelo abstracto llamado TimeStamped:
from django.utils import timezone
class TimeStamped(models.Model):
creation_date = models.DateTimeField(editable=False)
last_modified = models.DateTimeField(editable=False)
def save(self, *args, **kwargs):
if not self.creation_date:
self.creation_date = timezone.now()
self.last_modified = timezone.now()
return super(TimeStamped, self).save(*args, **kwargs)
class Meta:
abstract = True
Y luego, cuando desee un modelo que tenga este comportamiento de marca de tiempo, simplemente subclase:
MyNewTimeStampyModel(TimeStamped):
field1 = ...
Si desea que los campos se muestren en admin, simplemente elimine la opción editable=False
Creo que la solución más fácil (y quizás la más elegante) aquí es aprovechar el hecho de que puede establecer el default
en un llamable. Entonces, para evitar el manejo especial del administrador de auto_now, puedes declarar el campo así:
from django.utils import timezone
date_filed = models.DateField(default=timezone.now)
Es importante que no use timezone.now()
ya que el valor predeterminado no se actualizaría (es decir, el valor predeterminado se establece solo cuando se carga el código). Si te encuentras haciendo esto mucho, puedes crear un campo personalizado. Sin embargo, esto es bastante SECO ya lo creo.
Cualquier campo con el auto_now
atributos auto_now
también heredará editable=False
y, por lo tanto, no se mostrará en el panel de administración. Se ha hablado en el pasado sobre cómo hacer que auto_now_add
argumentos auto_now
y auto_now_add
, y aunque todavía existen, creo que es mejor usar un método personalizado save()
.
Por lo tanto, para que esto funcione correctamente, recomendaría no usar auto_now
o auto_now_add
y en su lugar, definiría su propio método save()
para asegurarse de que la created
se actualice solo si no se establece la id
(como cuando se crea el elemento por primera vez), y actualizarlo cada vez que se guarda el elemento.
He hecho exactamente lo mismo con otros proyectos que he escrito usando Django, y así save()
se vería así:
from django.utils import timezone
class User(models.Model):
created = models.DateTimeField(editable=False)
modified = models.DateTimeField()
def save(self, *args, **kwargs):
'''''' On save, update timestamps ''''''
if not self.id:
self.created = timezone.now()
self.modified = timezone.now()
return super(User, self).save(*args, **kwargs)
¡Espero que esto ayude!
Editar en respuesta a los comentarios:
La razón por la que me quedo con la sobrecarga de save()
frente a estos argumentos de campo es doble:
- Los mencionados altibajos con su fiabilidad. Estos argumentos dependen en gran medida de la forma en que cada tipo de base de datos con la que Django sabe cómo interactuar trata un campo de marca de fecha / hora, y parece romperse y / o cambiar entre cada versión. (Lo que creo es el ímpetu detrás de la llamada para eliminarlos por completo).
- El hecho de que solo funcionen en DateField, DateTimeField y TimeField, y al usar esta técnica, puede rellenar automáticamente cualquier tipo de campo cada vez que se guarda un elemento.
- Use
django.utils.timezone.now()
vs.datetime.datetime.now()
, ya que devolverá un objetodatetime.datetime
ingenioso para TZ o dependiente de lasettings.USE_TZ
.
Para resolver por qué el OP vio el error, no lo sé exactamente, pero parece que la created
no está poblada, a pesar de tener auto_now_add=True
. Para mí, se destaca como un error, y subraya el elemento # 1 en mi pequeña lista de arriba: auto_now
y auto_now_add
son auto_now_add
en el mejor de los casos.
En cuanto a su pantalla de administrador, vea esta respuesta .
Nota: auto_now y auto_now_add se configuran en editable = False de forma predeterminada, por lo que se aplica esto.
Hablando de una pregunta complementaria: si desea ver estos campos en admin (aunque no podrá editarlo), puede agregar readonly_fields
a su clase de administrador.
class SomeAdmin(ModelAdmin):
readonly_fields = ("created","modified",)
Bueno, esto se aplica solo a las últimas versiones de Django (creo, 1.3 y superior)
He encontrado una solución
Si tengo una clase modelo como:
class MyModel(models.Model):
time = models.DatetimeField(auto_now_add=True)
time.editable = True
Entonces este campo aparecerá en mi página de cambio de administrador
Necesitaba algo similar hoy en el trabajo. El valor predeterminado es timezone.now (), pero editable en las vistas de administración y de clase que heredan de FormMixin, por lo que para mi creación en my models.py el siguiente código cumplió con estos requisitos:
from __future__ import unicode_literals
import datetime
from django.db import models
from django.utils.functional import lazy
from django.utils.timezone import localtime, now
def get_timezone_aware_now_date():
return localtime(now()).date()
class TestDate(models.Model):
created = models.DateField(default=lazy(
get_timezone_aware_now_date, datetime.date)()
)
Para DateTimeField, supongo que elimine el .date () de la función y cambie datetime.date a datetime.datetime o mejor timezone.datetime. No lo he probado con DateTime, solo con Date.
Puede usar timezone.now()
para creado y auto_now
para modificado:
from django.utils import timezone
class User(models.Model):
created = models.DateTimeField(default=timezone.now())
modified = models.DateTimeField(auto_now=True)
Si está utilizando una clave principal personalizada en lugar del auto- increment int
predeterminado auto- increment int
, auto_now_add
provocará un error.
Aquí está el código del DateTimeField.pre_save predeterminado de Django con auto_now
y auto_now_add
:
def pre_save(self, model_instance, add):
if self.auto_now or (self.auto_now_add and add):
value = timezone.now()
setattr(model_instance, self.attname, value)
return value
else:
return super(DateTimeField, self).pre_save(model_instance, add)
No estoy seguro de lo que el parámetro add
es. Espero que sea algo como:
add = True if getattr(model_instance, ''id'') else False
El nuevo registro no tendrá
id
, por lo quegetattr(model_instance, ''id'')
devolverá False y no se establecerá ningún valor en el campo.
auto_now=True
no funcionó para mí en Django 1.4.1, pero el siguiente código me salvó. Es para el horario de la zona horaria.
from django.utils.timezone import get_current_timezone
from datetime import datetime
class EntryVote(models.Model):
voted_on = models.DateTimeField(auto_now=True)
def save(self, *args, **kwargs):
self.voted_on = datetime.now().replace(tzinfo=get_current_timezone())
super(EntryVote, self).save(*args, **kwargs)