Django auto_now y auto_now_add

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 se muestran. ¿Se espera eso?

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 el 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:

  1. 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 que sean eliminados por completo).
  2. 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.
  3. Utilice django.utils.timezone.now() frente a datetime.datetime.now() , porque devolverá un objeto datetime.datetime ingenioso para TZ o dependiente de la settings.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.

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 de django ), 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 ).

 from django.db import models from django.utils import timezone class AutoDateTimeField(models.DateTimeField): def pre_save(self, model_instance, add): return timezone.now() #usage created_at = models.DateField(default=timezone.now) updated_at = models.AutoDateTimeField(default=timezone.now) 

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)

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. Por lo tanto, para evitar el manejo especial de auto_now por parte del administrador, simplemente puede 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.

Si modifica su clase de modelo de esta manera:

 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

Según 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

¿Esto es motivo de preocupación?

No, Django lo agrega automáticamente al guardar los modelos, por lo que se espera.

Pregunta paralela: 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.

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 cuál es el parámetro add . Espero que sea algo como:

 add = True if getattr(model_instance, 'id') else False 

El nuevo registro no tendrá id , por lo que getattr(model_instance, 'id') devolverá False y no se establecerá ningún valor en el campo.

Necesitaba algo similar hoy en el trabajo. El valor predeterminado es timezone.now() , pero editable tanto en las vistas de administración como 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 .

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.

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) 

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 eg datetime.date.today() >>> datetime.datetime.now() + Added field created_date on myapp.User