214 votes

Comment limiter la valeur maximale d'un champ numérique dans un modèle Django ?

Django dispose de divers champs numériques pouvant être utilisés dans les modèles, par exemple DecimalField y PositiveIntegerField . Bien que la première puisse être limitée au nombre de décimales stockées et au nombre total de caractères stockés, existe-t-il un moyen de la restreindre au stockage de l'information sur l'état d'avancement des travaux ? uniquement des chiffres compris dans une certaine fourchette, par exemple 0,0-5,0 ?

Sinon, existe-t-il un moyen de restreindre un PositiveIntegerField pour qu'il ne stocke, par exemple, que des nombres jusqu'à 50 ?

Mise à jour : maintenant que le Bug 6845 a été fermé cette question de StackOverflow est peut-être sans objet. - sampablokuper

0 votes

Vous pourriez créer un signal de pré-enregistrement : http://docs.djangoproject.com/en/dev/ref/signals/#django.db.models.signals.pre_save

0 votes

J'aurais dû mentionner que je souhaite également que la restriction soit appliquée dans l'administration de Django. Pour obtenir cela, au moins, la documentation a ceci à dire : docs.djangoproject.com/fr/dev/ref/contrib/admin/

0 votes

En fait, avant la version 1.0 de Django, il semblait y avoir une solution très élégante : cotellese.net/2007/12/11/… . Je me demande s'il n'y a pas un moyen aussi élégant de faire cela dans la version svn de Django.

470voto

user1569050 Points 754

Vous pouvez utiliser Les validateurs intégrés de Django -

from django.db.models import IntegerField, Model
from django.core.validators import MaxValueValidator, MinValueValidator

class CoolModelBro(Model):
    limited_integer_field = IntegerField(
        default=1,
        validators=[
            MaxValueValidator(100),
            MinValueValidator(1)
        ]
     )

Modifier : Lorsque vous travaillez directement avec le modèle, assurez-vous d'appeler le modèle Nettoyage complet avant de sauvegarder le modèle afin de déclencher les validateurs. Ceci n'est pas nécessaire lorsque l'on utilise ModelForm puisque les formulaires le feront automatiquement.

4 votes

Je suppose que vous devez écrire votre propre validateur si vous voulez que le champ limited_integer_field soit également facultatif ? (Valider la plage uniquement si elle n'est pas vide) null=True, blank=True ne l'a pas fait

3 votes

Dans la configuration de Django 1.7 null=True y blank=True fonctionne comme prévu. Le champ est facultatif et s'il est laissé vide, il est enregistré comme nul.

143voto

NathanD Points 2433

Vous pouvez également créer un type de champ de modèle personnalisé - voir http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields

Dans ce cas, vous pouvez "hériter" du champ IntegerField intégré et modifier sa logique de validation.

Plus j'y pense, plus je réalise à quel point cela serait utile pour de nombreuses applications Django. Peut-être qu'un type IntegerRangeField pourrait être soumis comme patch pour que les développeurs de Django envisagent de l'ajouter au tronc.

Cela fonctionne pour moi :

from django.db import models

class IntegerRangeField(models.IntegerField):
    def __init__(self, verbose_name=None, name=None, min_value=None, max_value=None, **kwargs):
        self.min_value, self.max_value = min_value, max_value
        models.IntegerField.__init__(self, verbose_name, name, **kwargs)
    def formfield(self, **kwargs):
        defaults = {'min_value': self.min_value, 'max_value':self.max_value}
        defaults.update(kwargs)
        return super(IntegerRangeField, self).formfield(**defaults)

Ensuite, dans votre classe de modèle, vous l'utiliserez comme suit (le champ étant le module dans lequel vous avez placé le code ci-dessus) :

size = fields.IntegerRangeField(min_value=1, max_value=50)

OU pour une gamme de négatif et de positif (comme une gamme d'oscillateur) :

size = fields.IntegerRangeField(min_value=-100, max_value=100)

Ce qui serait vraiment cool, c'est qu'on puisse l'appeler avec l'opérateur de plage, comme ceci :

size = fields.IntegerRangeField(range(1, 50))

Mais, cela nécessiterait beaucoup plus de code puisque vous pouvez spécifier un paramètre 'skip' - range(1, 50, 2) - Idée intéressante cependant...

0 votes

Cela fonctionne mais dans la méthode de nettoyage du modèle, la valeur de l'entier est toujours None, ce qui fait que je ne peux pas lui fournir de nettoyage supplémentaire. Avez-vous une idée de la raison de ce problème et de la façon de le résoudre ?

3 votes

Vous pouvez améliorer votre champ personnalisé en ajoutant MinValueValidator(min_value) and MaxValueValidator(max_value) avant d'appeler super().__init__ ... (bribe : gist.github.com/madneon/147159f46ed478c71d5ee4950a9d697d )

102voto

congocongo Points 231
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator

size = models.IntegerField(validators=[MinValueValidator(0),
                                       MaxValueValidator(5)])

57voto

chrishaum Points 342

J'ai eu ce même problème ; voici ma solution :

SCORE_CHOICES = zip( range(1,n), range(1,n) )
score = models.IntegerField(choices=SCORE_CHOICES, blank=True)

13 votes

Utilisation d'une liste de compréhension : models.IntegerField(choices=[(i, i) for i in range(1, n)], blank=True)

10voto

tghw Points 14244

Il y a deux façons de procéder. La première consiste à utiliser la validation du formulaire pour ne jamais laisser un utilisateur saisir un nombre supérieur à 50. Documents sur la validation des formulaires .

Si aucun utilisateur n'est impliqué dans le processus, ou si vous n'utilisez pas de formulaire pour saisir les données, vous devrez remplacer l'option save pour déclencher une exception ou limiter les données qui entrent dans le champ.

2 votes

Vous pouvez également utiliser un formulaire pour valider les données non humaines. Il est très efficace de remplir le formulaire comme technique de validation polyvalente.

1 votes

Après avoir réfléchi à la question, je suis sûr que je ne veux pas placer la validation dans un formulaire. La question de savoir quelle plage de nombres est acceptable fait autant partie du modèle que la question de savoir quel type de nombre est acceptable. Je ne veux pas avoir à dire à chaque formulaire par lequel le modèle est éditable, quelle est la plage de nombres à accepter. Ce serait une violation de DRY, et de plus, c'est tout simplement inapproprié. Je vais donc chercher à surcharger la méthode de sauvegarde du modèle, ou peut-être créer un type de champ de modèle personnalisé - à moins que je ne trouve une méthode d'enregistrement de la valeur. même meilleure façon :)

0 votes

Tghw, vous avez dit que je pouvais "surcharger la méthode de sauvegarde du modèle pour lancer une exception ou limiter les données entrant dans le champ". Comment pourrais-je, à partir de la méthode save() de la définition du modèle, faire en sorte que si le nombre saisi est en dehors d'une plage donnée, l'utilisateur reçoive une erreur de validation, comme s'il avait saisi des caractères dans un champ numérique ? En d'autres termes, existe-t-il un moyen de faire cela qui fonctionnera indépendamment du fait que l'utilisateur modifie les données via l'administration ou via un autre formulaire ? Je ne veux pas simplement limiter les données qui entrent dans le champ sans en informer l'utilisateur :) Merci !

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