116 votes

Désactiver temporairement auto_now / auto_now_add

J'ai un modèle comme celui-ci:

 class FooBar(models.Model):
    createtime = models.DateTimeField(auto_now_add=True)
    lastupdatetime = models.DateTimeField(auto_now=True)
 

Je souhaite écraser les deux champs de date de certaines instances de modèle (utilisées lors de la migration de données). La solution actuelle ressemble à ceci:

 for field in new_entry._meta.local_fields:
    if field.name == "lastupdatetime":
        field.auto_now = False
    elif field.name == "createtime":
        field.auto_now_add = False

new_entry.createtime = date
new_entry.lastupdatetime = date
new_entry.save()

for field in new_entry._meta.local_fields:
    if field.name == "lastupdatetime":
        field.auto_now = True
    elif field.name == "createtime":
        field.auto_now_add = True
 

Y a-t-il une meilleure solution?

111voto

dirleyrls Points 407

J'ai récemment fait face à cette situation lors du test de mon application. Je devais "forcer" un horodatage expiré. Dans mon cas, j'ai fait le tour en utilisant une mise à jour de queryset. Comme ça:

 # my model
class FooBar(models.Model):
    title = models.CharField(max_length=255)
    updated_at = models.DateTimeField(auto_now=True, auto_now_add=True)



# my tests
foo = FooBar.objects.get(pk=1)

# force a timestamp
lastweek = datetime.datetime.now() - datetime.timedelta(days=7)
FooBar.objects.filter(pk=foo.pk).update(updated_at=lastweek)

# do the testing.
 

56voto

andreaspelme Points 1275

Vous ne pouvez pas vraiment désactiver auto_now/auto_now_add d'une autre façon que vous le faites déjà. Si vous avez besoin de flexibilité pour modifier ces valeurs, auto_now/auto_now_add n'est pas le meilleur choix. Il est souvent plus souple à utiliser default et/ou remplacer l' save() méthode pour faire la manipulation juste avant que l'objet est enregistré.

À l'aide de default et un substituée save() méthode, une façon de résoudre votre problème serait de définir votre modèle comme ceci:

class FooBar(models.Model):
    createtime = models.DateTimeField(default=datetime.datetime.now)
    lastupdatetime = models.DateTimeField()

    def save(self, *args, **kwargs):
        if not kwargs.pop('skip_lastupdatetime', False):
            self.lastupdatetime = datetime.datetime.now()

        super(FooBar, self).save(*args, **kwargs)

Dans votre code, si vous souhaitez ignorer la automatique lastupdatetime changer, il suffit d'utiliser

new_entry.save(skip_lastupdatetime=True)

Si votre objet est enregistré dans l'interface d'admin ou d'autres lieux, save() sera appelé sans le skip_lastupdatetime argument, et il va se comporter comme il le faisait avant avec auto_now.

29voto

Ballpark Points 1341

J'ai utilisé la suggestion faite par le demandeur et créé quelques fonctions. Voici le cas d'utilisation:

 turn_off_auto_now(FooBar, "lastupdatetime")
turn_off_auto_now_add(FooBar, "createtime")

new_entry.createtime = date
new_entry.lastupdatetime = date
new_entry.save()
 

Voici la mise en œuvre:

 def turn_off_auto_now(Clazz, field_name):
    def auto_now_off(field):
        field.auto_now = False
    iter_fields_and_do(Clazz, field_name, auto_now_off)

def turn_off_auto_now_add(Clazz, field_name):
    def auto_now_add_off(field):
        field.auto_now_add = False
    iter_fields_and_do(Clazz, field_name, auto_now_add_off)

def iter_fields_and_do(Clazz, field_name, func):
    for field in Clazz._meta.local_fields:
        if field.name == field_name:
            func(field)
 

Des fonctions similaires peuvent être créées pour les réactiver.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X