Désactivez 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 pour certaines instances de modèle (utilisées lors de la migration des 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?

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 jeu de requête. 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. 

Vous ne pouvez pas vraiment désactiver auto_now / auto_now_add d’une autre manière que vous ne 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 flexible d’utiliser default et / ou de remplacer la méthode save() pour effectuer une manipulation juste avant que l’object soit enregistré.

En utilisant default et une méthode save() surchargée, 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, où vous voulez ignorer le changement automatique de lastupdatetime, utilisez simplement

 new_entry.save(skip_lastupdatetime=True) 

Si votre object est enregistré dans l’interface d’administration ou à d’autres endroits, save () sera appelé sans l’argument skip_lastupdatetime, et il se comportera comme auparavant avec auto_now .

J’ai utilisé la suggestion du demandeur et créé certaines 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(ModelClass, field_name): def auto_now_off(field): field.auto_now = False do_to_model(ModelClass, field_name, auto_now_off) def turn_off_auto_now_add(ModelClass, field_name): def auto_now_add_off(field): field.auto_now_add = False do_to_model(ModelClass, field_name, auto_now_add_off) def do_to_model(ModelClass, field_name, func): field = ModelClass._meta.get_field_by_name(field_name)[0] func(field) 

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

Je suis allé au gestionnaire de contexte pour la réutilisation.

 @contextlib.contextmanager def suppress_autotime(model, fields): _original_values = {} for field in model._meta.local_fields: if field.name in fields: _original_values[field.name] = { 'auto_now': field.auto_now, 'auto_now_add': field.auto_now_add, } field.auto_now = False field.auto_now_add = False try: yield finally: for field in model._meta.local_fields: if field.name in fields: field.auto_now = _original_values[field.name]['auto_now'] field.auto_now_add = _original_values[field.name]['auto_now_add'] 

Utilisez comme ça:

 with suppress_autotime(my_object, ['updated']): my_object.some_field = some_value my_object.save() 

Boom.

Vous pouvez également utiliser le paramètre update_fields pour save() si vous connaissez les champs auxquels vous souhaitez le limiter et exclure les champs auto_now / auto_now_add:

https://docs.djangoproject.com/en/stable/ref/models/instances/#specifying-which-fields-to-save

Pour ceux qui regardent cela lorsqu’ils écrivent des tests, il y a une bibliothèque python appelée freezegun qui vous permet de simuler le temps – donc quand le code auto_now_add s’exécute, il obtient le temps que vous voulez réellement. Alors:

 from datetime import datetime, timedelta from freezegun import freeze_time with freeze_time('2016-10-10'): new_entry = FooBar.objects.create(...) with freeze_time('2016-10-17'): # use new_entry as you wish, as though it was created 7 days ago 

Il peut également être utilisé comme décorateur – voir le lien ci-dessus pour les documents de base.

Je devais désactiver auto_now pour un champ DateTime pendant une migration et je pouvais le faire.

 events = Events.objects.all() for event in events: for field in event._meta.fields: if field.name == 'created_date': field.auto_now = False event.save() 

Je suis en retard à la fête, mais comme plusieurs autres réponses, il s’agit d’une solution que j’ai utilisée lors d’une migration de firebase database. La différence avec les autres réponses est que cela désactive tous les champs auto_now pour le modèle en supposant qu’il n’y a vraiment aucune raison d’avoir plus d’un tel champ.

 def disable_auto_now_fields(*models): """Turns off the auto_now and auto_now_add atsortingbutes on a Model's fields, so that an instance of the Model can be saved with a custom value. """ for model in models: for field in model._meta.local_fields: if hasattr(field, 'auto_now'): field.auto_now = False if hasattr(field, 'auto_now_add'): field.auto_now_add = False 

Alors pour l’utiliser, vous pouvez simplement faire:

 disable_auto_now_fields(Document, Event, ...) 

Et il passera en revue et nuke tous vos champs auto_now et auto_now_add pour toutes les classes de modèle que vous passez.

copie de Django – Models.DateTimeField – Modification dynamic de la valeur auto_now_add

Eh bien, j’ai passé cet après-midi à découvrir et le premier problème est de savoir comment récupérer l’object modèle et où dans le code. Je suis dans restframework dans serializer.py, par exemple dans __init__ de sérialiseur il ne pouvait pas encore avoir le modèle. Maintenant, dans to_internal_value, vous pouvez obtenir la classe du modèle, après avoir obtenu le champ et après avoir modifié les propriétés du champ comme dans cet exemple:

 class ProblemSerializer(serializers.ModelSerializer): def to_internal_value(self, data): ModelClass = self.Meta.model dfil = ModelClass._meta.get_field('date_update') dfil.auto_now = False dfil.editable = True 

J’avais besoin d’une solution qui fonctionne avec update_or_create , je suis venu à cette solution basée sur le code @andreaspelme.

Le seul changement est que vous pouvez définir le saut en définissant le champ modifié pour skip non seulement la transmission de kwarg skip_modified_update à la méthode save ().

Juste yourmodelobject.modified='skip' et la mise à jour sera ignorée!

 from django.db import models from django.utils import timezone class TimeTrackableAbstractModel(models.Model): created = models.DateTimeField(default=timezone.now, db_index=True) modified = models.DateTimeField(default=timezone.now, db_index=True) class Meta: abstract = True def save(self, *args, **kwargs): skip_modified_update = kwargs.pop('skip_modified_update', False) if skip_modified_update or self.modified == 'skip': self.modified = models.F('modified') else: self.modified = timezone.now() super(TimeTrackableAbstractModel, self).save(*args, **kwargs)