2009-05-11 23 views
109

Django udostępnia różne pola numeryczne do użycia w modelach, np. DecimalField i PositiveIntegerField. Chociaż ten pierwszy może być ograniczony do liczby zapisanych miejsc dziesiętnych i całkowitej liczby zapisanych znaków, czy istnieje sposób ograniczenia tego do zapisywania liczb w określonym zakresie, np. 0,0-5,0?Jak ograniczyć maksymalną wartość pola numerycznego w modelu Django?

W przypadku braku tego sposobu można ograniczyć wartość pola PositiveIntegerField do przechowywania, na przykład liczby do 50?

Aktualizacja: teraz błąd 6845 has been closed, to pytanie StackOverflow może być dyskusyjne. - sampablokuper

+0

Powinienem wspomnieć, że chcę również, aby ograniczenie zostało zastosowane w administratorze Django. Aby to uzyskać, przynajmniej dokumentacja ma to do powiedzenia: http://docs.djangoproject.com/en/dev/ref/contrib/admin/#adding-custom-validation-to-the-admin – sampablokuper

+0

W rzeczywistości, pre -1,0 Django wydaje się mieć naprawdę eleganckie rozwiązanie: http://www.cotellese.net/2007/12/11/adding-model-field-validation-to-the-django-admin-page/. Zastanawiam się, czy istnieje równie elegancki sposób robienia tego w wersji svn Django. – sampablokuper

+0

Jestem zawiedziony, gdy się dowiem, że * nie * wydaje się być eleganckim sposobem na zrobienie tego z obecnym Django svn. Więcej informacji można znaleźć w tym wątku dyskusji: http://groups.google.com/group/django-users/browse_thread/thread/7f23e90d9d3aedd4 – sampablokuper

Odpowiedz

111

Można również utworzyć niestandardowy typ pola modelu - patrz http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields

W tym przypadku można „dziedziczyć” z wbudowanym IntegerField i zastąpić jego logiki walidacji.

Im więcej o tym myślę, zdaję sobie sprawę, jak użyteczne byłoby to w przypadku wielu aplikacji Django. Być może typ IntegerRangeField może zostać przesłany jako łata dla Django do rozważenia dodania do trunkingu.

To działa dla mnie:

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) 

Następnie w swojej klasie model, by używać go tak (pole jest moduł, w którym można umieścić powyższy kod):

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

OR dla zakresu ujemnego i dodatniego (jak zakres oscylatora):

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

Co byłoby naprawdę fajne, to jeśli ould nazwać z operatorem zakresu tak:

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

Ale to będzie wymagało dużo więcej kodu, ponieważ, ponieważ można określić parametr „przeskoczyć” - zakres (1, 50, 2) - ciekawy pomysł chociaż. ..

+4

Myślę, że tak długo ponieważ bilet # 6845 pozostaje otwarty, jest to najlepsze rozwiązanie. http://code.djangoproject.com/ticket/6845 – sampablokuper

+0

To działa, ale gdy w czystej metodzie modelu, wartość liczby całkowitej jest zawsze Brak, co sprawia, że ​​nie mogę zapewnić żadnego dodatkowego czyszczenia do niego. Każdy pomysł, dlaczego tak jest i jak to naprawić? – KrisF

10

Istnieją dwa sposoby, aby to zrobić. Jedną z nich jest użycie sprawdzania poprawności formularza, aby nigdy nie dopuszczać do liczby przekraczającej 50 użytkowników. Form validation docs.

Jeśli nie ma żadnego użytkownika zaangażowanego w proces lub nie używasz formularza do wprowadzania danych, musisz zastąpić metodę modelu save, aby rzucić wyjątek lub ograniczyć dane docierające do pola.

+2

Możesz użyć formularza dla sprawdzanie poprawności także danych wejściowych innych niż ludzkie. Świetnie sprawdza się w wypełnianiu Form jako uniwersalnej techniki walidacji. –

+0

Po przemyśleniu tego, jestem pewien, że nie chcę umieszczać walidacji w formie. Kwestia, który zakres liczb jest akceptowalny, jest w takim samym stopniu częścią modelu, jak pytanie, który rodzaj liczby jest akceptowalny. Nie chcę mówić o każdej formie, za pomocą której model jest edytowalny, tylko o tym, który zakres liczb do zaakceptowania. To naruszyłoby DRY, a poza tym jest po prostu niewłaściwe. Więc przyjrzę się przesłonięciem metody zapisu modelu lub, być może, utworzenia niestandardowego typu pola modelu - chyba że znajdę * jeszcze * lepszy sposób :) – sampablokuper

+0

tghw, powiedziałeś, że mogę "nadpisać metodę zapisu modelu rzucić wyjątek lub ograniczyć dane wchodzące na pole. " W jaki sposób ja - w ramach zdekodowanej metody save() definicji modelu - sprawi, że jeśli wprowadzona liczba znajdzie się poza podanym zakresem, użytkownik otrzyma błąd weryfikacji tak, jakby wprowadziła dane znakowe do pola liczbowego? To znaczy. czy jest jakiś sposób, w jaki mogę to zrobić, niezależnie od tego, czy użytkownik edytuje za pośrednictwem administratora, czy za pośrednictwem innego formularza? Nie chcę po prostu ograniczać danych w terenie bez informowania użytkownika o tym, co się dzieje :) Dzięki! – sampablokuper

48

Miałem ten sam problem; tutaj było moje rozwiązanie:

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

Używanie rozumienia list: 'models.IntegerField (opcje = [(i, i) dla i w zakresie (1, n)], puste = True)' –

+1

To nie działa na poziomie bazy danych. – YPCrumble

209

Można użyć Django's built-in validators -

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) 
     ] 
    ) 

Edytuj: Chociaż te only work when you're using the model in a ModelForm, jeśli nie używasz modelu "na własną rękę", westchnienie.

+3

Chyba musisz napisać swój własny walidator, jeśli chcesz, by limit_integer_field był opcjonalny? (Tylko zakres Validate, jeśli nie jest pusty) null = True, puste = True nie wykonało tego. – radtek

+0

W Django 1.7 ustawienie 'null = True' i' blank = True' działa zgodnie z oczekiwaniami. Pole jest opcjonalne i jeśli jest puste, jest przechowywane jako puste. –

60
from django.db import models 
from django.core.validators import MinValueValidator, MaxValueValidator 

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

To nie działa dobrze, jeśli chcesz, aby pole było opcjonalne. Stworzyłem walidator, który wykonuje oba i pozwala przejść, jeśli pole jest puste. – radtek

4

Oto najlepsze rozwiązanie, jeśli potrzebujesz dodatkowej elastyczności i nie chcesz zmieniać pola modelu. Wystarczy dodać ten niestandardowy walidator:

#Imports 
from django.core.exceptions import ValidationError  

class validate_range_or_null(object): 
    compare = lambda self, a, b, c: a > c or a < b 
    clean = lambda self, x: x 
    message = ('Ensure this value is between %(limit_min)s and %(limit_max)s (it is %(show_value)s).') 
    code = 'limit_value' 

    def __init__(self, limit_min, limit_max): 
     self.limit_min = limit_min 
     self.limit_max = limit_max 

    def __call__(self, value): 
     cleaned = self.clean(value) 
     params = {'limit_min': self.limit_min, 'limit_max': self.limit_max, 'show_value': cleaned} 
     if value: # make it optional, remove it to make required, or make required on the model 
      if self.compare(cleaned, self.limit_min, self.limit_max): 
       raise ValidationError(self.message, code=self.code, params=params) 

i może być stosowany jako taki:

class YourModel(models.Model): 

    .... 
    no_dependents = models.PositiveSmallIntegerField("How many dependants?", blank=True, null=True, default=0, validators=[validate_range_or_null(1,100)]) 

Oba parametry są max i min, a to pozwala na wartości null. Możesz dostosować walidator, jeśli chcesz, pozbywając się zaznaczonej instrukcji if lub zmieniając puste pole = False, null = False w modelu. Będzie to oczywiście wymagało migracji.

Uwaga: Musiałem dodać walidator, ponieważ Django nie sprawdza poprawności zakresu na PositiveSmallIntegerField, zamiast tego tworzy smallint (w postgres) dla tego pola i otrzymujesz błąd DB, jeśli podana wartość jest poza zakresem.

Mam nadzieję, że to pomaga :) Więcej o Validators in Django.

PS. Oparłem swoją odpowiedź na BaseValidator w django.core.validators, ale wszystko jest inne, z wyjątkiem kodu.

Powiązane problemy