Django set range for integer model field as constraint

Question:

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?

Asked By: Md. Tanvir Raihan

||

Answers:

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])
Answered By: utkbansal

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.
Answered By: JRodDynamite

If you are using postgres, you can use range fields to specify the range.
Check this: Range Fields in django

Answered By: Kishan Mehta

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",
            )
        ]
Answered By: 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

Answered By: Yevhen Bondar
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.