Pour Django 1.1.
J'ai ceci dans mon models.py:
class User(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
En mettant à jour une ligne, je reçois:
[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 partie pertinente de ma base de données est:
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
Est-ce une source de préoccupation?
Question secondaire: dans mon outil d'administration, ces deux champs n'apparaissent pas. Est-ce prévu?
Tout champ avec le jeu d'attributs auto_now
héritera également de _editable=False
_ et ne sera donc pas affiché dans le panneau d'administration. Il a été question dans le passé de faire disparaître les arguments _auto_now
_ et auto_now_add
, et même s'ils existent encore, je pense que vous valez mieux utiliser - méthode personnalisée save()
.
Donc, pour que cela fonctionne correctement, je vous recommande de ne pas utiliser _auto_now
_ ou _auto_now_add
_ et de définir votre propre méthode save()
pour vous assurer que created
n'est mis à jour que si id
n'est pas défini (par exemple, lorsque l'élément est créé pour la première fois) et doit le mettre à jour modified
à chaque fois que l'élément est enregistré.
J'ai fait exactement la même chose avec d'autres projets que j'ai écrits en utilisant Django, et votre save()
ressemblerait à ceci:
_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)
_
J'espère que cela t'aides!
Modifier en réponse aux commentaires:
La raison pour laquelle je reste juste avec la surcharge save()
contre l'utilisation de ces arguments de champ est double:
Django.utils.timezone.now()
contre datetime.datetime.now()
, car il renverra un objet _datetime.datetime
_ naïf ou naïf, dépendant de _settings.USE_TZ
_.Pour expliquer pourquoi l’opérateur a vu l’erreur, je ne le sais pas exactement, mais il semble que created
ne soit même pas rempli, bien que _auto_now_add=True
_. Pour moi, il s’agit d’un bogue et souligne le point 1 de ma petite liste ci-dessus: _auto_now
_ et _auto_now_add
_ sont au mieux floconneux.
Mais je voulais souligner que l’opinion exprimée dans le réponse acceptée est quelque peu dépassée. Selon des discussions plus récentes (bogues Django # 7634 et # 12785 ), auto_now et auto_now_add ne vont nulle part, et même si vous passez à la discussion originale , vous trouverez des arguments solides contre le RY (comme dans DRY) dans les méthodes de sauvegarde personnalisées.
Une meilleure solution a été proposée (types de champs personnalisés), mais elle n’a pas gagné suffisamment d’élan pour être intégrée à Django. Vous pouvez écrire le vôtre en trois lignes (c'est suggestion 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)
Parlant d'une question secondaire: si vous voulez voir ces champs dans admin (vous ne pourrez pas le modifier), vous pouvez ajouter readonly_fields
à votre classe admin.
class SomeAdmin(ModelAdmin):
readonly_fields = ("created","modified",)
Cela ne concerne que les dernières versions de Django (je crois, versions 1.3 et supérieures).
Je pense que la solution la plus simple (et peut-être la plus élégante) consiste à tirer parti du fait que vous pouvez définir default
sur un appelable. Donc, pour contourner le traitement spécial de auto_now par l'administrateur, vous pouvez simplement déclarer le champ de la manière suivante:
from Django.utils import timezone
date_filed = models.DateField(default=timezone.now)
Il est important que vous n'utilisiez pas timezone.now()
car la valeur par défaut ne serait pas mise à jour (c'est-à-dire, la valeur par défaut n'est définie que lorsque le code est chargé). Si vous vous trouvez souvent en train de le faire, vous pouvez créer un champ personnalisé. Cependant, c'est joli DRY déjà je pense.
Si vous modifiez votre classe de modèle comme ceci:
class MyModel(models.Model):
time = models.DateTimeField(auto_now_add=True)
time.editable = True
Ensuite, ce champ apparaîtra dans la page de changement de mon administrateur
D'après ce que j'ai lu et mon expérience avec Django jusqu'à présent, auto_now_add est un buggy. Je suis d'accord avec le jhanisme - écrasez la méthode de sauvegarde normale, c'est propre et vous savez ce qui se passe. Maintenant, pour le rendre sec, créez un modèle abstrait appelé 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
Et ensuite, lorsque vous voulez un modèle qui a ce comportement obsolète, il suffit de sous-classe:
MyNewTimeStampyModel(TimeStamped):
field1 = ...
Si vous voulez que les champs apparaissent dans admin, supprimez simplement l'option editable=False
Est-ce une source de préoccupation?
Non, Django l'ajoute automatiquement pour vous lors de l'enregistrement des modèles. C'est donc prévu.
Question secondaire: dans mon outil d'administration, ces 2 champs ne s'affichent pas. Est-ce prévu?
Étant donné que ces champs sont ajoutés automatiquement, ils ne sont pas affichés.
Pour ajouter à ce qui précède, comme le dit Synack, il y a eu un débat sur la liste de diffusion Django afin de la supprimer, car elle n'est "pas bien conçue" et est "un hack".
Écrire une sauvegarde personnalisée () sur chacun de mes modèles est beaucoup plus pénible que d’utiliser auto_now
De toute évidence, vous n'êtes pas obligé de l'écrire dans tous les modèles. Vous pouvez l'écrire dans un modèle et en hériter d'autres.
Mais, comme auto_add
et auto_now_add
sont là, je les utiliserais plutôt que d'essayer d'écrire une méthode moi-même.
Vous pouvez utiliser timezone.now()
pour créer et auto_now
pour modifier:
from Django.utils import timezone
class User(models.Model):
created = models.DateTimeField(default=timezone.now())
modified = models.DateTimeField(auto_now=True)
Si vous utilisez une clé primaire personnalisée au lieu de la valeur par défaut auto- increment int
, auto_now_add
conduira à un bogue.
Voici le code par défaut de Django: DateTimeField.pre_save withauto_now
et 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)
Je ne suis pas sûr de ce que le paramètre add
est. J'espère que ça va quelque chose comme:
add = True if getattr(model_instance, 'id') else False
Le nouvel enregistrement n'aura pas attr
id
, doncgetattr(model_instance, 'id')
retournera False, ce qui conduira à ne pas définir de valeur dans le champ.
En ce qui concerne votre affichage Admin, voir cette réponse .
Remarque: auto_now
et auto_now_add
sont réglés sur editable=False
par défaut, raison pour laquelle cela s'applique.
J'avais besoin de quelque chose de similaire aujourd'hui au travail. La valeur par défaut est timezone.now()
, mais modifiable à la fois dans les vues d'administrateur et de classe héritées de FormMixin
, le code suivant a donc été créé dans mon models.py
:
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)()
)
Pour DateTimeField
, je suppose que supprimer la .date()
de la fonction et modifier datetime.date
en datetime.datetime
ou mieux timezone.datetime
. Je n'ai pas essayé avec DateTime
, seulement avec Date
.
auto_now=True
ne fonctionnait pas pour moi dans Django 1.4.1, mais le code ci-dessous m'a sauvé. C'est pour le fuseau horaire prenant en compte la date et l'heure.
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)