Comment définir la valeur par défaut d’un champ de modèle Django à un appel de fonction / callable (par exemple, une date relative à l’heure de la création de l’object du modèle)

ÉDITÉ:

Comment définir un champ Django par défaut sur une fonction évaluée à chaque fois qu’un nouvel object modèle est créé?

Je veux faire quelque chose comme ce qui suit, sauf que dans ce code, le code est évalué une fois et définit la valeur par défaut à la même date pour chaque object de modèle créé, plutôt que d’évaluer le code chaque fois qu’un object modèle est créé:

from datetime import datetime, timedelta class MyModel(models.Model): # default to 1 day from now my_date = models.DateTimeField(default=datetime.now() + timedelta(days=1)) 

ORIGINAL:

Je veux créer une valeur par défaut pour un paramètre de fonction tel qu’il soit dynamic et qu’il soit appelé et défini chaque fois que la fonction est appelée. Comment puis je faire ça? par exemple,

 from datetime import datetime def mydate(date=datetime.now()): print date mydate() mydate() # prints the same thing as the previous call; but I want it to be a newer value 

Plus précisément, je veux le faire dans Django, par exemple,

 from datetime import datetime, timedelta class MyModel(models.Model): # default to 1 day from now my_date = models.DateTimeField(default=datetime.now() + timedelta(days=1)) 

La question est erronée. Lorsque vous créez un champ de modèle dans Django, vous ne définissez pas de fonction. Les valeurs par défaut de la fonction ne sont donc pas pertinentes:

 from datetime import datetime, timedelta class MyModel(models.Model): # default to 1 day from now my_date = models.DateTimeField(default=datetime.now() + timedelta(days=1)) 

Cette dernière ligne ne définit pas de fonction; il appelle une fonction pour créer un champ dans la classe.

PRE Django 1.7

Django vous permet de passer un appelable par défaut , et il l’invoquera à chaque fois, comme vous le souhaitez:

 from datetime import datetime, timedelta class MyModel(models.Model): # default to 1 day from now my_date = models.DateTimeField(default=lambda: datetime.now() + timedelta(days=1)) 

Django 1.7+

Veuillez noter que depuis Django 1.7, l’utilisation de lambda comme valeur par défaut n’est pas recommandée (cf commentaire @stvnw). La bonne façon de faire est de déclarer une fonction avant le champ et de l’utiliser comme un appelable dans default_value nommé arg:

 from datetime import datetime, timedelta # default to 1 day from now def get_default_my_date(): return datetime.now() + timedelta(days=1) class MyModel(models.Model): my_date = models.DateTimeField(default=get_default_my_date) 

Plus d’informations dans la réponse @simanas ci-dessous

Faire ceci default=datetime.now()+timedelta(days=1) est absolument faux!

Il est évalué lorsque vous démarrez votre instance de django. Si vous êtes sous Apache, cela fonctionnera probablement, car sur certaines configurations, Apache révoque votre application django à chaque requête, mais vous pouvez tout de même vous retrouver un jour à la recherche de votre code et essayer de comprendre pourquoi .

La bonne façon de faire est de passer un object appelable à l’argument par défaut. Cela peut être une fonction datetime.today ou votre fonction personnalisée. Ensuite, il est évalué chaque fois que vous demandez une nouvelle valeur par défaut.

 def get_deadline(): return datetime.today() + timedelta(days=20) class Bill(models.Model): name = models.CharField(max_length=50) customer = models.ForeignKey(User, related_name='bills') date = models.DateField(default=datetime.today) deadline = models.DateField(default=get_deadline) 

Il existe une distinction importante entre les deux constructeurs DateTimeField suivants:

 my_date = models.DateTimeField(auto_now=True) my_date = models.DateTimeField(auto_now_add=True) 

Si vous utilisez auto_now_add=True dans le constructeur, le datetime référencé par my_date est “immutable” (défini une seule fois lorsque la ligne est insérée dans la table).

Avec auto_now=True , toutefois, la valeur datetime sera mise à jour chaque fois que l’object est enregistré.

C’était définitivement un piège pour moi à un moment donné. Pour référence, les documents sont ici:

https://docs.djangoproject.com/en/dev/ref/models/fields/#datetimefield

Vous ne pouvez pas le faire directement; la valeur par défaut est évaluée lorsque la définition de la fonction est évaluée. Mais il y a deux façons de le contourner.

Tout d’abord, vous pouvez créer (puis appeler) une nouvelle fonction à chaque fois.

Ou, plus simplement, utilisez simplement une valeur spéciale pour marquer la valeur par défaut. Par exemple:

 from datetime import datetime def mydate(date=None): if date is None: date = datetime.now() print date 

Si None est une valeur de paramètre parfaitement raisonnable, et qu’il n’y a pas d’autre valeur raisonnable à sa place, vous pouvez simplement créer une nouvelle valeur qui ne fait pas partie du domaine de votre fonction:

 from datetime import datetime class _MyDateDummyDefault(object): pass def mydate(date=_MyDateDummyDefault): if date is _MyDateDummyDefault: date = datetime.now() print date del _MyDateDummyDefault 

Dans certains cas rares, vous écrivez un méta-code qui doit vraiment pouvoir prendre absolument n’importe quoi, même, disons, mydate.func_defaults[0] . Dans ce cas, vous devez faire quelque chose comme ceci:

 def mydate(*args, **kw): if 'date' in kw: date = kw['date'] elif len(args): date = args[0] else: date = datetime.now() print date 

Passez la fonction en paramètre au lieu de transmettre le résultat de l’appel de fonction.

C’est, au lieu de cela:

 def myfunc(date=datetime.now()): print date 

Essaye ça:

 def myfunc(date=datetime.now): print date() 

Parfois, vous devrez peut-être accéder aux données du modèle après avoir créé un nouveau modèle d’utilisateur.

Voici comment je génère un jeton pour chaque nouveau profil d’utilisateur en utilisant les 4 premiers caractères de leur nom d’utilisateur:

 from django.dispatch import receiver class Profile(models.Model): auth_token = models.CharField(max_length=13, default=None, null=True, blank=True) @receiver(post_save, sender=User) # this is called after a User model is saved. def create_user_profile(sender, instance, created, **kwargs): if created: # only run the following if the profile is new new_profile = Profile.objects.create(user=instance) new_profile.create_auth_token() new_profile.save() def create_auth_token(self): import random, ssortingng auth = self.user.username[:4] # get first 4 characters in user name self.auth_token = auth + ''.join(random.SystemRandom().choice(ssortingng.ascii_uppercase + ssortingng.digits + ssortingng.ascii_lowercase) for _ in range(random.randint(3, 5)))