Django auto now et auto now ajouter

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)

lors de la mise à jour d'une ligne j'obtiens:

[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 préoccupant?

question secondaire: dans mon outil d'administration, ces deux champs n'apparaissent pas. Qui est attendu?

199
demandé sur Peter Mortensen 2009-11-15 11:47:56

12 réponses

tout champ avec l'ensemble d'attributs auto_now héritera aussi editable=False et par conséquent n'apparaîtra pas 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 bien qu'ils existent toujours, je pense que vous êtes mieux en utilisant simplement une coutume save() méthode .

Donc, pour faire ce travail correctement, je recommandez de ne pas utiliser auto_now ou auto_now_add et définissez plutôt votre propre méthode save() pour vous assurer que created n'est mis à jour que si id n'est pas défini (comme lorsque l'élément est créé pour la première fois), et faites-le mettre à jour modified chaque fois que l'élément est sauvegardé.

j'ai fait exactement la même chose avec D'autres projets que J'ai écrits en utilisant Django, et donc 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)

espérons que aide!

modifier en réponse aux commentaires:

la raison pour laquelle je m'en tiens à la surcharge save() vs. se fier à ces arguments de terrain est double:

  1. les hauts et les bas mentionnés ci-dessus avec leur fiabilité. Ces arguments dépendent fortement de la façon dont chaque type de base de données avec laquelle Django sait interagir traite un champ Date/heure, et semble casser et / ou le changement entre chaque version. (Je crois que c'est l'élan derrière l'appel de les avoir supprimés).
  2. le fait qu'ils ne fonctionnent que sur DateField, DateTimeField, et TimeField, et en utilisant cette technique, vous êtes en mesure de peupler automatiquement n'importe quel type de champ chaque fois qu'un élément est enregistré.
  3. utilisez django.utils.timezone.now() vs. datetime.datetime.now() , car il retournera un objet TZ-aware ou naïf datetime.datetime en fonction de settings.USE_TZ .

pour expliquer pourquoi L'OP a vu l'erreur, je ne sais pas exactement, mais il semble que created n'est même pas peuplé du tout, malgré avoir auto_now_add=True . Pour moi, il se distingue comme un bug, et souligne l'article #1 dans ma petite liste ci-dessus: auto_now et auto_now_add sont feuilletés au mieux.

283
répondu jathanism 2018-03-04 02:43:00

Bah... Pas assez de réputation pour commentaire... Mais je voulais souligner que l'opinion exprimée dans l'acceptation réponse est quelque peu obsolète. Selon des discussions plus récentes (django bugs #7634 et #12785 ), auto_now et auto_now_add ne vont nulle part, et même si vous allez à la discussion originale , vous trouverez des arguments forts contre le RY (comme dans DRY) dans les méthodes de sauvegarde personnalisées.

une meilleure solution a été proposée (types de terrain sur mesure), mais n'a pas gagné assez d'élan pour en faire django. Vous pouvez écrire les vôtres en trois lignes (c'est la suggestion 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)
133
répondu Shai Berger 2014-06-10 20:06:23

en parlant d'une question secondaire: si vous voulez voir ces champs dans admin (cependant, vous ne pourrez pas les éditer), vous pouvez ajouter readonly_fields à votre classe admin.

class SomeAdmin(ModelAdmin):
    readonly_fields = ("created","modified",)

Eh bien, cela s'applique seulement aux dernières versions Django (je crois, 1.3 et plus haut)

26
répondu DataGreed 2012-07-25 09:25:41

je pense que la solution la plus facile (et peut-être la plus élégante) ici est de tirer parti du fait que vous pouvez définir default à un appelable. Donc, pour contourner la manipulation spéciale d'auto_now par l'administrateur, vous pouvez simplement déclarer le champ comme suit:

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 (i.e., la valeur par défaut est définie seulement lorsque le code est chargé). Si vous vous retrouver à faire beaucoup, vous pouvez créer un champ personnalisé. Cependant, c'est déjà assez sec je pense.

17
répondu Josh 2013-10-10 16:49:08

basé sur ce que j'ai lu et mon expérience avec Django jusqu'à présent, auto_now_add est buggy. Je suis d'accord avec le jthanisme --- annuler la méthode de sauvegarde normale c'est propre et vous savez ce qui se passe. Maintenant, pour le faire sécher, créez un modèle abstrait appelé Timestamp:

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 puis, quand vous voulez un modèle qui a ce comportement temporel, juste sous-classe:

MyNewTimeStampyModel(TimeStamped):
    field1 = ...

si vous voulez que les champs apparaissent dans admin, alors il suffit de supprimer le editable=False option

12
répondu Edward Newell 2014-09-03 22:18:00

j'ai trouvé une solution

si j'ai une classe de modèle comme:

class MyModel(models.Model):
    time = models.DatetimeField(auto_now_add=True)
    time.editable = True

alors ce champ apparaîtra dans ma page de changement d'administrateur

12
répondu Eric Zheng 2016-12-20 20:36:38

vous pouvez utiliser timezone.now() pour créé et auto_now pour modifié:

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 clé par défaut auto- increment int , auto_now_add entraînera un bogue.

voici le code par défaut de Django DateTimeField.pré_save avec auto_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 qu'est le paramètre add . J'espère que c' est-ce que quelque chose comme:

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

Le nouvel enregistrement n'aura pas attr id , donc getattr(model_instance, 'id') retournera False conduira à ne pas fixer une valeur dans le champ.

5
répondu simple_human 2014-10-01 03:04:17

est-ce préoccupant?

Non, Django l'ajoute automatiquement pour vous tout en sauvegardant les modèles, donc, il est attendu.

question secondaire: dans mon outil d'administration, ces 2 champs n'apparaissent pas. Qui est attendu?

puisque ces champs sont ajoutés automatiquement, ils ne sont pas affichés.

pour ajouter à ce qui précède, comme synack l'a dit, il y a eu un débat sur la liste de diffusion django pour supprimer cela, parce que, il est "pas bien conçu" et est" un hack "

écrire une sauvegarde personnalisée () sur chacun de mes modèles est beaucoup plus douloureux que d'utiliser l'auto_now

Évidemment, vous n'avez pas à écrire à chaque modèle. Vous pouvez l'écrire sur un modèle hériter d'autres.

, Mais, comme auto_add et auto_now_add sont là, je les utiliserais plutôt que d'essayer d'écrire un méthode de moi-même.

4
répondu Lakshman Prasad 2009-11-15 13:09:26

comme pour l'affichage de votre administrateur, voir cette réponse .

Note: auto_now et auto_now_add sont définis à editable=False par défaut, ce qui explique pourquoi cela s'applique.

2
répondu jlovison 2017-05-23 12:10:45

j'avais besoin de quelque chose de semblable aujourd'hui au travail. Valeur par défaut fuseau horaire.maintenant (), mais modifiable à la fois dans les vues admin et de classe héritant de FormMixin, donc pour created dans mon models.py le code suivant remplissait ces conditions:

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 supprimer le .date (), de la fonction et le changement de type datetime.date datetime.datetime ou meilleur fuseau horaire.datetime. Je n'ai pas essayé avec DateTime, seulement avec Date.

2
répondu Tiphareth 2016-04-26 15:21:37

auto_now=True N'a pas fonctionné pour moi dans Django 1.4.1, mais le code ci-dessous m'a sauvé. C'est pour la DateTime du fuseau horaire.

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)
1
répondu Ryu_hayabusa 2014-10-01 03:02:48

Voici la réponse si vous utilisez south et que vous voulez par défaut la date à laquelle vous ajoutez le champ à la base de données:

choisir l'option 2 puis: datetime.datetime.maintenant ()

ressemble à ceci:

$ ./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
-1
répondu Jeff Hoye 2013-05-21 14:46:11