Làm cách nào để giới hạn giá trị tối đa của trường số trong mô hình Django?


169

Django có nhiều trường số khác nhau có sẵn để sử dụng trong các mô hình, ví dụ DecimalFieldpositiveIntegerField . Mặc dù trước đây có thể được giới hạn số lượng vị trí thập phân được lưu trữ và tổng số ký tự được lưu trữ, có cách nào để hạn chế chỉ lưu trữ các số trong một phạm vi nhất định, ví dụ 0,0-5.0 không?

Không có điều đó, có cách nào để hạn chế một positiveIntegerField chỉ lưu trữ số lượng lên tới 50 không?

Cập nhật: bây giờ Bug 6845 đã bị đóng , câu hỏi StackOverflow này có thể được đưa ra. - sampablokuper



Tôi nên đã đề cập rằng tôi cũng muốn hạn chế được áp dụng trong quản trị viên của Django. Để có được điều đó, ít nhất, tài liệu có điều này để nói: docs.djangoproject.com/en/dev/ref/contrib/admin/ Kẻ
sampablokuper

Trên thực tế, Django trước 1.0 dường như đã có một giải pháp thực sự tao nhã: cotellese.net/2007/12/11/iêu . Tôi tự hỏi nếu có một cách thanh lịch không kém để làm điều này trong bản phát hành svn của Django.
sampablokuper

Tôi thất vọng khi biết rằng dường như không có một cách thanh lịch nào để làm điều này với Django svn hiện tại. Xem chủ đề thảo luận này để biết thêm chi tiết: Groups.google.com/group/django-users/browse_thread/thread/ Kẻ
sampablokuper

Sử dụng trình xác nhận trên mô hình và xác thực sẽ hoạt động trong giao diện quản trị viên và trong ModelForms: docs.djangoproject.com/en/dev/ref/validators/ Lỗi
guettli

Câu trả lời:


133

Bạn cũng có thể tạo loại trường mô hình tùy chỉnh - xem http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields

Trong trường hợp này, bạn có thể 'kế thừa' từ IntegerField tích hợp và ghi đè logic xác thực của nó.

Càng nghĩ nhiều về điều này, tôi càng nhận ra điều này hữu ích như thế nào đối với nhiều ứng dụng Django. Có lẽ loại IntegerRangeField có thể được gửi dưới dạng bản vá cho các nhà phát triển Django để xem xét thêm vào thân cây.

Điều này làm việc cho tôi:

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)

Sau đó, trong lớp mô hình của bạn, bạn sẽ sử dụng nó như thế này (trường là mô-đun nơi bạn đặt mã ở trên):

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

HOẶC cho một phạm vi âm và dương (như phạm vi dao động):

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

Điều tuyệt vời là nếu nó có thể được gọi với toán tử phạm vi như thế này:

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

Nhưng, điều đó sẽ đòi hỏi nhiều mã hơn vì bạn có thể chỉ định tham số 'bỏ qua' - phạm vi (1, 50, 2) - Ý tưởng thú vị mặc dù ...


Điều này hoạt động nhưng khi trong phương thức sạch của mô hình, giá trị của số nguyên luôn luôn là Không làm cho nó vì vậy tôi không thể cung cấp bất kỳ sự làm sạch bổ sung nào cho nó. Bất cứ ý tưởng tại sao điều này là và làm thế nào để khắc phục nó?
KrisF

2
Bạn có thể nâng cao trường tùy chỉnh của mình bằng cách thêm MinValueValidator(min_value) and MaxValueValidator(max_value)trước khi gọi super().__init__ ...(đoạn trích: gist.github.com/madneon/147159f46ed478c71d5ee4950a9d697d )
madneon

348

Bạn có thể sử dụng trình xác nhận tích hợp của 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)
        ]
     )

Chỉnh sửa : Khi làm việc trực tiếp với mô hình, đảm bảo gọi phương thức full_clean của mô hình trước khi lưu mô hình để kích hoạt trình xác nhận hợp lệ. Điều này là không bắt buộc khi sử dụng ModelFormvì các biểu mẫu sẽ tự động làm điều đó.


4
Tôi đoán bạn phải viết trình xác nhận của riêng bạn nếu bạn muốn giới hạn_integer_field là tùy chọn? (Chỉ xác thực phạm vi nếu không trống) null = True, blank = True không làm điều đó ..
radtek

2
Trong cài đặt Django 1.7 null=Trueblank=Truehoạt động như mong đợi. Trường là tùy chọn và nếu để trống, nó được lưu dưới dạng null.
Tim Tonomall

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

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

52

Tôi đã có vấn đề rất giống nhau; đây là giải pháp của tôi:

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

10
Sử dụng một danh sách hiểu:models.IntegerField(choices=[(i, i) for i in range(1, n)], blank=True)
Razzi Abuissa

10

Có hai cách để làm điều này. Một là sử dụng xác thực mẫu để không bao giờ cho phép bất kỳ số nào trên 50 được nhập bởi người dùng. Tài liệu xác nhận mẫu .

Nếu không có người dùng tham gia vào quá trình hoặc bạn không sử dụng biểu mẫu để nhập dữ liệu, thì bạn sẽ phải ghi đè savephương thức của mô hình để ném ngoại lệ hoặc giới hạn dữ liệu đi vào trường.


2
Bạn cũng có thể sử dụng một biểu mẫu để xác nhận đầu vào không phải của con người. Nó hoạt động tuyệt vời để điền vào Mẫu như là một kỹ thuật xác thực toàn diện.
S.Lott

1
Sau khi suy nghĩ về điều này, tôi khá chắc chắn rằng tôi không muốn đưa xác nhận vào một biểu mẫu. Câu hỏi về phạm vi số nào được chấp nhận là một phần của mô hình cũng như câu hỏi về loại số nào được chấp nhận. Tôi không muốn nói với mọi hình thức mà mô hình có thể chỉnh sửa được, chỉ cần chấp nhận phạm vi số nào. Điều này sẽ vi phạm DRY, và bên cạnh đó, nó hoàn toàn không phù hợp. Vì vậy, tôi sẽ xem xét việc ghi đè phương thức lưu của mô hình hoặc có thể tạo loại trường mô hình tùy chỉnh - trừ khi tôi có thể tìm thấy một cách thậm chí tốt hơn :)
sampablokuper

tghw, bạn nói rằng tôi có thể "ghi đè phương thức lưu của mô hình để ném ngoại lệ hoặc giới hạn dữ liệu đi vào trường." Làm thế nào tôi - từ trong phương thức overriden save () của định nghĩa mô hình - làm cho nó sao cho nếu số được nhập nằm ngoài một phạm vi nhất định, người dùng sẽ nhận được lỗi xác thực nhiều như thể cô ấy đã nhập dữ liệu ký tự vào trường số? Tức là có một số cách tôi có thể làm điều này sẽ hoạt động bất kể người dùng đang chỉnh sửa thông qua quản trị viên hoặc thông qua một số hình thức khác? Tôi không muốn giới hạn dữ liệu đi vào trường mà không cho người dùng biết chuyện gì đang xảy ra :) Cảm ơn!
sampablokuper

Ngay cả khi bạn sử dụng phương thức "lưu", điều này sẽ không hoạt động khi cập nhật bảng thông qua Truy vấn như MyModel.object.filter (blabla) .update (blabla) sẽ không gọi lưu nên sẽ không có kiểm tra nào được thực hiện
Olivier Pons

5

Đây là giải pháp tốt nhất nếu bạn muốn linh hoạt hơn và không muốn thay đổi trường mô hình của mình. Chỉ cần thêm trình xác nhận tùy chỉnh này:

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

Và nó có thể được sử dụng như vậy:

class YourModel(models.Model):

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

Hai tham số là tối đa và tối thiểu, và nó cho phép null. Bạn có thể tùy chỉnh trình xác nhận nếu bạn muốn bằng cách loại bỏ câu lệnh if được đánh dấu hoặc thay đổi trường của bạn thành blank = false, null = false trong mô hình. Điều đó tất nhiên sẽ yêu cầu di chuyển.

Lưu ý: Tôi đã phải thêm trình xác thực vì Django không xác thực phạm vi trên positiveSmallIntegerField, thay vào đó, nó tạo ra một phần nhỏ (tính theo postgres) cho trường này và bạn gặp lỗi DB nếu số được chỉ định nằm ngoài phạm vi.

Hy vọng điều này sẽ giúp :) Thêm về Trình xác nhận trong Django .

Tái bút Tôi dựa trên câu trả lời của mình trên BaseValidator trong django.core.validators, nhưng mọi thứ đều khác ngoại trừ mã.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.