Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django set range for integer model field as constraint

I have a django model,

class MyModel(models.Model)
    qty = model.IntegerField()

where I want to set constraint for qty something like this, >0 or <0,i.e the qty can be negative or positive but can not be 0.

Is there any straight forward way to do this in Django?

like image 971
Md. Tanvir Raihan Avatar asked Nov 18 '15 06:11

Md. Tanvir Raihan


4 Answers

You can use Django's built-in validators -

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

class MyModel(models.Model):
    qty = models.IntegerField(
        default=1,
        validators=[MaxValueValidator(100), MinValueValidator(1)]
     )

NOTE:

  1. The validators will not run automatically when you save a model, but if you are using a ModelForm, it will run your validators on the fields that are included in the form. Check this link for more info.
  2. To enforce constraints on a database level refer to maertejin's answer below.
like image 59
JRodDynamite Avatar answered Oct 17 '22 12:10

JRodDynamite


You will have to create a custom validator

from django.core.exceptions import ValidationError

def validate_number(value):
    if something :  # Your conditions here
        raise ValidationError('%s some error message' % value)

And then use this validator in your model

from django.db import models

class MyModel(models.Model):
    field = models.IntegerField(validators=[validate_number])
like image 31
utkbansal Avatar answered Oct 17 '22 13:10

utkbansal


Since Django 2.2 you can enforce the constraints on a database level with CheckConstraint:

class MyModel(models.Model):
    qty = models.IntegerField()

    class Meta:
        constraints = [
            models.CheckConstraint(
                check=models.Q(qty__gte=1) & models.Q(qty__lt=10),
                name="A qty value is valid between 1 and 10",
            )
        ]
like image 6
maerteijn Avatar answered Oct 17 '22 14:10

maerteijn


For better code reusability you can create custom RangeIntegerField

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


class RangeIntegerField(models.IntegerField):
    def __init__(self, *args, **kwargs):
        validators = kwargs.pop("validators", [])
        
        # turn min_value and max_value params into validators
        min_value = kwargs.pop("min_value", None)
        if min_value is not None:
            validators.append(MinValueValidator(min_value))
        max_value = kwargs.pop("max_value", None)
        if max_value is not None:
            validators.append(MaxValueValidator(max_value))

        kwargs["validators"] = validators

        super().__init__(*args, **kwargs)

You can use this Field in your models

class SomeModel(models.Model):
    some_value = RangeIntegerField(min_value=42, max_value=451)

It plays well with both django-forms and DRF's serializers

like image 1
Eugenij Avatar answered Oct 17 '22 12:10

Eugenij