วิธี จำกัด มูลค่าสูงสุดของเขตข้อมูลตัวเลขในรุ่น Django


169

Django มีเขตข้อมูลที่เป็นตัวเลขต่างๆที่มีอยู่สำหรับการใช้งานในรูปแบบเช่นDecimalFieldและPositiveIntegerField แม้ว่าอดีตจะถูก จำกัด จำนวนตำแหน่งทศนิยมที่จัดเก็บและจำนวนตัวอักษรโดยรวมที่เก็บไว้มีวิธีใดที่จะ จำกัด การจัดเก็บเฉพาะตัวเลขภายในช่วงที่กำหนดเช่น 0.0-5.0?

ความล้มเหลวนั้นมีวิธีใดบ้างที่จะ จำกัด PositiveIntegerField ให้เก็บเฉพาะเช่นจำนวนสูงสุด 50 หรือไม่

อัปเดต: ตอนนี้ที่ Bug 6845 ถูกปิดแล้วคำถาม StackOverflow นี้อาจจะเป็นข้อสงสัย - sampablokuper


คุณสามารถสร้างสัญญาณก่อนบันทึก: http://docs.djangoproject.com/en/dev/ref/signals/#django.db.models.signals.pre_save
igorgue

ฉันควรจะกล่าวว่าฉันต้องการ จำกัด การใช้ในผู้ดูแลระบบของ Django ด้วย สำหรับการรับอย่างน้อยเอกสารมีสิ่งนี้จะพูดว่า: docs.djangoproject.com/en/dev/ref/contrib/admin/…
sampablokuper

อันที่จริงก่อน 1.0 Django ดูเหมือนว่าจะมีทางออกที่สวยงามจริงๆ: cotellese.net/2007/12/11/... ฉันสงสัยว่ามีวิธีที่สง่างามเท่ากันในการทำเช่นนี้ในการปล่อย svn ของ Django
sampablokuper

ผมผิดหวังที่จะเรียนรู้ว่ามีไม่ได้ดูเหมือนจะเป็นวิธีที่สง่างามที่จะทำเช่นนี้กับ Django SVN ปัจจุบัน ดูหัวข้อสนทนานี้สำหรับรายละเอียดเพิ่มเติม: groups.google.com/group/django-users/browse_thread/thread/…
sampablokuper

ใช้เครื่องมือตรวจสอบความถูกต้องของโมเดลและการตรวจสอบจะทำงานในอินเทอร์เฟซผู้ดูแลระบบและใน ModelForms: docs.djangoproject.com/en/dev/ref/validators/…
guettli

คำตอบ:


133

คุณสามารถสร้างประเภทฟิลด์โมเดลที่กำหนดเองได้ - ดูhttp://docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields

ในกรณีนี้คุณสามารถ 'รับช่วง' จาก IntegerField ในตัวและแทนที่ตรรกะการตรวจสอบ

ยิ่งฉันคิดถึงสิ่งนี้มากเท่าไหร่ฉันก็รู้ว่ามันจะมีประโยชน์กับแอพ Django มากแค่ไหน บางทีประเภท IntegerRangeField อาจถูกส่งเป็นปะสำหรับ Django devs เพื่อพิจารณาเพิ่มลงใน trunk

นี่ใช้งานได้สำหรับฉัน:

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)

จากนั้นในคลาสโมเดลของคุณคุณจะใช้มันเช่นนี้ (ฟิลด์เป็นโมดูลที่คุณใส่โค้ดด้านบน):

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

หรือสำหรับช่วงของค่าลบและค่าบวก (เช่นช่วงออสซิลเลเตอร์):

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

สิ่งที่จะเจ๋งจริงๆคือถ้ามันสามารถเรียกด้วยตัวดำเนินการช่วงดังนี้:

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

แต่นั่นจะต้องใช้รหัสมากขึ้นเนื่องจากคุณสามารถระบุพารามิเตอร์ 'ข้าม' - ช่วง (1, 50, 2) - แนวคิดที่น่าสนใจแม้ว่า ...


วิธีนี้ใช้งานได้ แต่เมื่ออยู่ในวิธีการล้างข้อมูลของรุ่นค่าของจำนวนเต็มจะเป็น None ซึ่งทำให้ไม่สามารถทำความสะอาดเพิ่มเติมได้ ความคิดใดว่าทำไมถึงเป็นแบบนี้และจะแก้ไขได้อย่างไร?
KrisF

2
คุณสามารถปรับปรุงฟิลด์ที่กำหนดเองของคุณโดยเพิ่มMinValueValidator(min_value) and MaxValueValidator(max_value)ก่อนโทรsuper().__init__ ...(ตัวอย่าง: gist.github.com/madneon/147159f46ed478c71d5ee4950a9d697d )
madneon

349

คุณสามารถใช้เครื่องมือตรวจสอบในตัวของ 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)
        ]
     )

แก้ไข : เมื่อทำงานกับโมเดลโดยตรงต้องแน่ใจว่าได้เรียกเมธอดfull_cleanก่อนบันทึกโมเดลเพื่อทริกเกอร์ตัวตรวจสอบความถูกต้อง สิ่งนี้ไม่จำเป็นเมื่อใช้ModelFormเนื่องจากแบบฟอร์มจะทำโดยอัตโนมัติ


4
ฉันเดาว่าคุณต้องเขียน validator ของคุณเองถ้าคุณต้องการให้ limited_integer_field เป็นตัวเลือกเช่นกัน? (เฉพาะช่วงการตรวจสอบถ้าไม่ว่าง) = null ทรูว่างเปล่า = True ไม่ได้ทำมัน ..
radtek

2
ใน Django 1.7 การตั้งค่าnull=Trueและblank=Trueทำงานตามที่คาดไว้ ฟิลด์นี้เป็นทางเลือกและถ้าเว้นว่างไว้ฟิลด์นั้นจะถูกจัดเก็บเป็นโมฆะ
Tim Tisdall

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

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

52

ฉันมีปัญหาเดียวกันนี้มาก นี่คือทางออกของฉัน:

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

10
ใช้รายการความเข้าใจ:models.IntegerField(choices=[(i, i) for i in range(1, n)], blank=True)
Razzi Abuissa

10

มีสองวิธีในการทำเช่นนี้ หนึ่งคือการใช้การตรวจสอบแบบฟอร์มเพื่อไม่ให้ผู้ใช้ป้อนจำนวนเกิน 50 แบบฟอร์มการตรวจสอบเอกสารเอกสารตรวจสอบรูปแบบ

หากไม่มีผู้ใช้ที่เกี่ยวข้องในกระบวนการหรือคุณไม่ได้ใช้แบบฟอร์มเพื่อป้อนข้อมูลคุณจะต้องแทนที่saveวิธีการของแบบจำลองเพื่อโยนข้อยกเว้นหรือ จำกัด ข้อมูลที่จะเข้าสู่ฟิลด์


2
คุณสามารถใช้แบบฟอร์มสำหรับการตรวจสอบการป้อนข้อมูลที่ไม่ใช่ของมนุษย์ได้เช่นกัน มันใช้งานได้ดีในการเติมแบบฟอร์มเป็นเทคนิคการตรวจสอบทุกรอบ
S.Lott

1
หลังจากคิดเกี่ยวกับเรื่องนี้ฉันค่อนข้างแน่ใจว่าฉันไม่ต้องการที่จะนำการตรวจสอบในรูปแบบ คำถามที่ช่วงของตัวเลขเป็นที่ยอมรับนั้นเป็นส่วนหนึ่งของแบบจำลองมากเท่ากับคำถามที่ยอมรับได้ ฉันไม่ต้องการที่จะบอกทุกรูปแบบว่าแบบจำลองนั้นสามารถแก้ไขได้ซึ่งเป็นช่วงที่ต้องยอมรับตัวเลข สิ่งนี้จะเป็นการละเมิด DRY และยิ่งกว่านั้นมันไม่เหมาะสมเลย ดังนั้นฉันจะมองเข้าไปเอาชนะรูปแบบวิธีการประหยัดหรือบางทีอาจจะสร้างรูปแบบประเภทของฟิลด์ที่กำหนดเอง - ถ้าฉันสามารถหาแม้กระทั่งวิธีที่ดีกว่า :)
sampablokuper

คุณบอกว่าฉันสามารถ "แทนที่วิธีการบันทึกของแบบจำลองเพื่อโยนข้อยกเว้นหรือ จำกัด ข้อมูลที่จะลงในฟิลด์" ฉันจะ - จากภายใน overriden save () method ของคำจำกัดความแบบจำลองได้อย่างไรเพื่อให้ถ้าหมายเลขที่ป้อนนั้นอยู่นอกช่วงที่กำหนดผู้ใช้จะได้รับข้อผิดพลาดในการตรวจสอบความถูกต้องเหมือนกับว่าเธอป้อนข้อมูลอักขระลงในฟิลด์ตัวเลข คือมีวิธีที่ฉันสามารถทำได้ซึ่งจะทำงานโดยไม่คำนึงว่าผู้ใช้แก้ไขผ่านผู้ดูแลหรือผ่านรูปแบบอื่น ๆ ? ฉันไม่ต้องการ จำกัด ข้อมูลที่จะลงสนามโดยไม่บอกผู้ใช้ว่าเกิดอะไรขึ้น :) ขอบคุณ!
sampablokuper

แม้ว่าคุณจะใช้วิธี "บันทึก" วิธีนี้จะไม่ทำงานเมื่ออัปเดตตารางผ่าน QuerySet เช่น MyModel.object.filter (blabla) .update (blabla) จะไม่เรียกบันทึกดังนั้นจึงไม่มีการตรวจสอบ
Olivier Pons

5

นี่คือทางออกที่ดีที่สุดหากคุณต้องการความยืดหยุ่นเป็นพิเศษและไม่ต้องการเปลี่ยนฟิลด์โมเดลของคุณ เพียงเพิ่มเครื่องมือตรวจสอบที่กำหนดเองนี้:

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

และมันสามารถใช้เป็นเช่น:

class YourModel(models.Model):

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

พารามิเตอร์สองตัวคือ max และ min และอนุญาตให้มีค่า Null คุณสามารถปรับแต่งเครื่องมือตรวจสอบความถูกต้องได้หากต้องการโดยกำจัดเครื่องหมายคำสั่ง if หรือเปลี่ยนฟิลด์ของคุณให้ว่าง = False, null = False ในโมเดล แน่นอนว่าจะต้องมีการโยกย้าย

หมายเหตุ: ฉันต้องเพิ่มตัวตรวจสอบความถูกต้องเพราะ Django ไม่ได้ตรวจสอบช่วงใน PositiveSmallIntegerField แทนมันจะสร้าง smallint (เป็น postgres) สำหรับเขตข้อมูลนี้และคุณได้รับข้อผิดพลาด DB หากตัวเลขที่ระบุนั้นอยู่นอกช่วง

หวังว่านี้ช่วย :) เพิ่มเติมเกี่ยวกับเครื่องมือตรวจสอบใน Django

PS ฉันตอบตาม BaseValidator ใน django.core.validators แต่ทุกอย่างแตกต่างกันยกเว้นรหัส

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.