Bir Django modelindeki sayısal alanın maksimum değeri nasıl sınırlandırılır?


169

Django, DecimalField ve PositiveIntegerField gibi modellerde kullanılabilen çeşitli sayısal alanlara sahiptir . Birincisi depolanan ondalık basamak sayısı ve depolanan toplam karakter sayısı ile sınırlandırılabilse de, onu yalnızca belirli bir aralıktaki sayıları depolamakla sınırlamanın bir yolu var mı, örneğin 0.0-5.0?

Başarısız olursa, PositiveIntegerField öğesinin yalnızca 50'ye kadar olan sayıları depolamasını kısıtlamanın bir yolu var mı?

Güncelleme: Bug 6845 kapatıldığı için , bu StackOverflow sorusu tartışmalı olabilir. - sampablokuper



Kısıtlamanın Django'nun yöneticisinde de uygulanmasını istediğimi söylemeliydim. Bunu elde etmek için, en azından, belgelerin şunu söylemesi gerekir: docs.djangoproject.com/en/dev/ref/contrib/admin/…
sampablokuper

Aslında 1.0 öncesi Django'nun gerçekten zarif bir çözümü varmış gibi görünüyor: cotellese.net/2007/12/11/… . Acaba bunu Django'nun svn sürümünde yapmanın eşit derecede zarif bir yolu var mı?
sampablokuper

Mevcut Django svn ile bunu yapmak için zarif bir yol olmadığını görünmeyi hayal kırıklığına uğrattı . Daha fazla ayrıntı için bu tartışma konusuna bakın: groups.google.com/group/django-users/browse_thread/thread/…
sampablokuper

Modelde doğrulayıcılar kullanın ve doğrulama yönetici arayüzünde ve ModelForms'da çalışır: docs.djangoproject.com/en/dev/ref/validators/…
guettli

Yanıtlar:


133

Özel bir model alan türü de oluşturabilirsiniz - bkz. Http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields

Bu durumda, yerleşik IntegerField öğesinden 'devralabilir' ve doğrulama mantığını geçersiz kılabilirsiniz.

Bunu daha çok düşündüğümde, bunun birçok Django uygulaması için ne kadar yararlı olacağını anlıyorum. Belki de bir IntegerRangeField türü, Django geliştiricileri için gövdeye eklemeyi düşünmeleri için bir yama olarak gönderilebilir.

Bu benim için çalışıyor:

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)

Daha sonra model sınıfınızda, bunu şu şekilde kullanırsınız (alan yukarıdaki kodu koyduğunuz modüldür):

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

VEYA bir dizi negatif ve pozitif için (bir osilatör aralığı gibi):

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

Gerçekten harika olan şey, bu gibi aralık operatörü ile çağrılabilmesidir:

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

Ancak, bir 'atlama' parametresi - aralık (1, 50, 2) - İlginç bir fikir belirtebildiğiniz için çok daha fazla kod gerekir ...


Bu çalışır, ancak modelin temiz yönteminde, tamsayı değeri her zaman Hiçbiri'dir, bu da onu ek temizlik sağlayamaz. Bunun neden olduğu ve nasıl düzeltileceği hakkında bir fikriniz var mı?
KrisF

2
AramadanMinValueValidator(min_value) and MaxValueValidator(max_value) önce ekleyerek özel alanınızı geliştirebilirsiniz super().__init__ ...(snippet: gist.github.com/madneon/147159f46ed478c71d5ee4950a9d697d )
madneon

350

Sen kullanabilirsiniz Django yerleşik bulunuyor doğrulayıcıları -

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

Düzenleme : Doğrudan modelle çalışırken , doğrulayıcıları tetiklemek için modeli kaydetmeden önce model full_clean yöntemini çağırdığınızdan emin olun . ModelFormFormlar bunu otomatik olarak yapacağından dolayı bu gerekli değildir .


4
Eğer sınırlı_integer_field da isteğe bağlı olmasını istiyorsanız kendi doğrulayıcı yazmak zorunda sanırım? (Yalnızca boş değilse aralığı doğrula) null = Doğru, boş = Doğru yapmadı ..
radtek

2
Django 1.7 ayarında null=Trueve blank=Truebeklendiği gibi çalışıyor. Alan isteğe bağlıdır ve boş bırakılırsa boş olarak saklanır.
Tim Tisdall

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

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

52

Ben de aynı problemi yaşadım; İşte benim çözümüm:

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

10
Bir liste kavrayışı kullanma:models.IntegerField(choices=[(i, i) for i in range(1, n)], blank=True)
Razzi Abuissa

10

Bunu yapmanın iki yolu vardır. Bunlardan biri, kullanıcı tarafından 50'nin üzerinde herhangi bir sayının girilmesine asla izin vermemek için form doğrulama kullanmaktır. Form doğrulama belgeleri .

İşleme dahil olan kullanıcı yoksa veya veri girmek için bir form kullanmıyorsanız, savebir istisna atmak veya alana giden verileri sınırlamak için modelin yöntemini geçersiz kılmanız gerekir .


2
İnsan olmayan girişi doğrulamak için de bir form kullanabilirsiniz. Formu çok yönlü bir doğrulama tekniği olarak doldurmak harika çalışır.
S.Lott

1
Bunu düşündükten sonra, doğrulamayı bir forma koymak istemediğimden eminim. Hangi sayı aralığının kabul edilebilir olduğu sorusu, hangi sayı türünün kabul edilebilir olduğu sorusu kadar modelin bir parçasıdır. Modelin düzenlenebilir olduğu her forma, sadece hangi sayı aralığını kabul edeceğimi söylemek istemiyorum. Bu DRY'yi ihlal eder ve ayrıca, sadece uygunsuzdur. Bu yüzden, modelin kaydetme yöntemini geçersiz
kılmaya

tghw, "bir istisna atmak veya alana giren verileri sınırlamak için modelin kaydetme yöntemini geçersiz kılabileceğimi" söylediniz. Model tanımının geçersiz kılınan save () yönteminden nasıl girerim ki, girilen sayı belirli bir aralığın dışındaysa, kullanıcı karakter verilerini sayısal alana girmiş gibi bir doğrulama hatası alır mı? Yani kullanıcının yönetici veya başka bir form aracılığıyla düzenleme yapmasına bakılmaksızın çalışacak bir yolu var mı? Sadece kullanıcıya neler olduğunu söylemeden alana giren verileri sınırlamak istemiyorum :) Teşekkürler!
sampablokuper

"Save" yöntemini kullansanız bile, tabloyu MyModel.object.filter (blabla) .update (blabla) gibi QuerySet üzerinden güncellerken kaydetmeyecektir, bu nedenle kontrol yapılmaz
Olivier Pons

5

Ekstra esneklik istiyorsanız ve model alanınızı değiştirmek istemiyorsanız en iyi çözüm. Bu özel doğrulayıcıyı eklemeniz yeterlidir:

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

Ve bu şekilde kullanılabilir:

class YourModel(models.Model):

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

İki parametre max ve min'dir ve null değerlerine izin verir. İşaretli if ifadesinden kurtularak veya alanınızı boş olarak değiştirerek doğrulayıcıyı özelleştirebilirsiniz = False, null = False modelde. Bu elbette bir göç gerektirecektir.

Not: Django PositiveSmallIntegerField üzerindeki aralığı doğrulamadığından doğrulayıcı eklemek zorunda kaldım, bunun yerine bu alan için bir smallint (postgres olarak) oluşturur ve belirtilen sayısal aralık dışındaysa bir DB hatası alırsınız.

Umarım bu yardımcı olur :) Django Validators hakkında daha fazlası .

PS. Cevabımı django.core.validators içindeki BaseValidator'a dayandım, ancak kod hariç her şey farklı.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.