2

This is my simple Django class:

class MyClass(models.Model):
    my_field1 = models.IntegerField(null=False,blank=False,) 

    # I want to insert validation rule such that my_field2 = True iff my_field1 > 10
    my_field2 = models.BooleanField(null=False, blank=False,)

I would like to insert validation rule such that my_field2 = True iff my_field1 > 10. If the user enters data that violates that constraint, I would like the constructor to raise an exception.

#MyClass.object.create(9, False)     # Should Succeed
#MyClass.object.create(11, True)     # Should Succeed
#MyClass.object.create(31, True)     # Should Succeed
#MyClass.object.create(8, True)      # Should Throw exception
#MyClass.object.create(21, False)    # Should Throw exception

How can I do it? Everything I have read about django validation occurs in the model form. But my application has no forms. I need the validation in the model itself. How can I do that?

Saqib Ali
  • 11,931
  • 41
  • 133
  • 272
  • 1
    Take a look at [pre-save signals](https://docs.djangoproject.com/en/1.8/ref/signals/#pre-save). – Leistungsabfall Jul 29 '15 at 18:42
  • have a look at [this on Django's docs](https://docs.djangoproject.com/en/1.8/ref/models/instances/#validating-objects) – Pynchia Jul 29 '15 at 18:49
  • Pynchia, that doesn't solve the problem because the `clean` methods are not automatically called when I do `MyClass.object.create()` – Saqib Ali Jul 29 '15 at 18:51

4 Answers4

3

Following the answer here. I think you are looking for something like this:

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

class MyClass(models.Model):
    my_field1 = IntegerField(
        validators=[MinValueValidator(11)]
    )
Community
  • 1
  • 1
Eric Bulloch
  • 827
  • 6
  • 10
  • You're right, this is something like what I'm looing for. However, This shows how to do simple min and max validation. I want to do something more complex. How can I do custom validation? And it has to use the values of the other parameters being sent in (as described in my original question) – Saqib Ali Jul 29 '15 at 20:07
  • It sounds like you want to write a custom validator. You can read about them here: https://docs.djangoproject.com/en/1.8/ref/validators/ – Eric Bulloch Jul 29 '15 at 20:18
  • EricBulloch, but can those custom validators validate based on the values of the other fields coming into the constructor? From the link you posted, it doesn't seem so. – Saqib Ali Jul 29 '15 at 22:40
  • @SaqibAli According to the link the validators are not ran when a model is saved https://docs.djangoproject.com/en/1.8/ref/validators/#how-validators-are-run. It looks like you will have to do something like override the save function as well. – Eric Bulloch Jul 30 '15 at 05:42
1

You can attach a pre_save signal and check if those two values are satisfying your constraints.

from django.core.exceptions import ValidationError
from django.db.models.signals import pre_save
from django.dispatch import receiver

@receiver(pre_save, sender=MyClass)
def model_pre_save(sender, instance, **kwargs):
    if ((instance.my_field1 >= 10 and not instance.my_field2) or
        (instance.my_field1 < 10 and instance.my_field2)):
        raise ValidationError('Validation error: {}'.format(instance))
Ozgur Vatansever
  • 49,246
  • 17
  • 84
  • 119
0

Django provides Model Validation options with one gotcha: they don't fire by default, so you have to override the model's save() method to call clean/ full_clean like so:

def save(self, *args, **kwargs):
    self.full_clean()
    super(MyClass, self).save(*args, **kwargs)
Tom
  • 22,301
  • 5
  • 63
  • 96
0

Why you dont use the Form layer? You could use the form layer as a "validation" layer only, it means, dont use it to render an HTML. Its pretty common to use the Form layer even on JSON API or AJAX.

For example, you could use a form as follow:

def my_ajax_view(request, pk):
    ret = {'success': False, 'errors': []}
    instance = get_object_or_404(MyModel, pk=pk)
    f = ModelUpdateForm(request.POST, instance=ins)
    if f.is_valid():
        #your code
        ret['success'] = True
        return JSONResponse(ret)

    ret['errors'] = serialize_form_errors(f)  #not in django
    return JSONResponse(ret)

Other approach could be re-implement the save() method, as explained in @tom's comment.

Thanks!

Martin Alderete
  • 736
  • 5
  • 9