Allow only positive decimal numbers
PythonDjangoPython Problem Overview
Within my Django models I have created a decimal field like this:
price = models.DecimalField(_(u'Price'), decimal_places=2, max_digits=12)
Obviously it makes no sense for the price to be negative or zero. Is there a way to limit the decimal number to only positive numbers?
Or do I have to capture this using form validation?
Python Solutions
Solution 1 - Python
Use the MinValueValidator
.
price = models.DecimalField(_(u'Price'), decimal_places=2, max_digits=12, validators=[MinValueValidator(Decimal('0.01'))])
Solution 2 - Python
You could do something like this :
# .....
class priceForm(ModelForm):
price = forms.DecimalField(required=False, max_digits=6, min_value=0)
This, also, is responsible for the validator value of 'price'.
Solution 3 - Python
In Django 2.2 you can add constraints to a model which will be applied in the migrations as a constraint on the database table:
from decimal import Decimal
from django.db import models
class Item(models.Model):
price = models.DecimalField( _(u'Price'), decimal_places=2, max_digits=12 )
class Meta:
constraints = [
models.CheckConstraint(check=models.Q(price__gt=Decimal('0')), name='price_gt_0'),
]
> Validation of Constraints
>
> In general constraints are not checked during full_clean()
, and do not raise ValidationErrors
. Rather you’ll get a database integrity error on save()
.
Solution 4 - Python
Assuming this is your product model, and you want to add non-negative constraint on price field. You can add the meta constraint on the model:
class Product(models.Model):
price = models.DecimalField(max_digits=13, decimal_places=2)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
managed = True
db_table = 'product'
constraints = [
models.CheckConstraint(check=models.Q(price__gte='0'), name='product_price_non_negative'),
]
Solution 5 - Python
According to the docs, it seems that there's no way to put something like a database constraint on a field. The best you can do is add model "validators" which will be called if you call model validation or use a ModelForm
. The validators are skipped if you simply put values into an object and save()
.
So, you can add validation on the forms or you can add validation to the model which also get run on the form level if you use ModelForm
.
From the docs on "How validators are run":
> See the form validation for more information on how validators are run
> in forms, and Validating objects for how they’re run in models. Note
> that validators will not be run automatically when you save a model,
> but if you are using a ModelForm
, it will run your validators on any
> fields that are included in your form. See the ModelForm documentation
> for information on how model validation interacts with forms.
Solution 6 - Python
- Import dependencies
from decimal import Decimal
from django.core.validators import MinValueValidator
- Add validators
price = models.DecimalField(
decimal_places=2,
max_digits=12,
validators=[MinValueValidator(Decimal('0.01'))]
)