Django REST Framework Validators

3 minute read

My helpful screenshot Validation in REST framework is entirely performed on serializer class. There are no any hidden behavior on model instance. All of the validation rule can be exactly seen by printing the representation of serializer.

Lets make the model for customer report record

1
2
3
4
5
6
7
from django.utils import timezone
from django.db import models

class CustomerReportRecord(models.Model):
time_raised = models.DateTimeField(default=timezone.now, editable=False)
reference = models.CharField(unique=True, max_length=20)
description = models.TextField()

Now let’s make the model serializer for validation

1
2
3
4
5
6
7
from rest_framework import serializers
from .models import CustomerReportRecord

class CustomerReportSerializer(serializers.ModelSerializer):
class Meta:
model = CustomerReportRecord
fields = "**all**"

Representation of CustomerReportSerializer

1
2
3
4
5
6
7
8
> > > from app.serializers import CustomerReportSerializer
> > > CustomerReportSerializer()
> > > CustomerReportSerializer():

    id = IntegerField(label='ID', read_only=True)
    time_raised = DateTimeField(read_only=True)
    reference = CharField(max_length=20, validators=[<UniqueValidator(queryset=CustomerReportRecord.objects.all())>])
    description = CharField(style={'base_template': 'textarea.html'})

The unique = true validation of reference field in model has been imposed by a validators. The validators used is Unique Validator derived from REST Framework.We can impose same validation without defining validation on model.

Lets make model again without any validation

1
2
3
4
5
6
7
from django.utils import timezone
from django.db import models

class CustomerReportRecord(models.Model):
time_raised = models.DateTimeField(default=timezone.now, editable=False)
reference = models.CharField(max_length=20)
description = models.TextField()

Now there is no any validation written on model. But we will get same behaviour and representation by over riding the serializer.

1
2
3
4
5
6
7
8
9
10
from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from .models import CustomerReportRecord

class CustomerReportSerializer(serializers.ModelSerializer):
reference = serializers.CharField(max_length=20, validators=[UniqueValidator(queryset=CustomerReportRecord.objects.all())])

    class Meta:
        model = CustomerReportRecord
        fields = "__all__"

Here the definition of reference in model has been changed by over riding the reference field. The UniqueValidator is imported from the REST framework and if we look at the representation of the serializer, it gives the same result.

1
2
3
4
5
6
7
8
> > > from app.serializers import CustomerReportSerializer
> > > CustomerReportSerializer()
> > > CustomerReportSerializer():

    id = IntegerField(label='ID', read_only=True)
    reference = CharField(max_length=20, validators=[<UniqueValidator(queryset=<QuerySet []>)>])
    time_raised = DateTimeField(read_only=True)
    description = CharField(style={'base_template': 'textarea.html'})

On top of that, custom message can be set on validation error. The unique validator accepts message field which takes string and is raised on validation error instead of a default message which is “This field must be unique.”

Every validation can be written without using model let’s impose unique together only using serializer.Normally we write unique_together = True under the meta class of model to achieve unique together validation.

1
2
3
4
5
6
7
8
9
10
11
from django.utils import timezone
from django.db import models


class CustomerReportRecord(models.Model):
    time_raised = models.DateTimeField(default=timezone.now, editable=False)
    reference = models.CharField(max_length=20)
    description = models.TextField()

    class Meta:
        unique_together = ['reference', 'description']
1
2
3
4
5
6
7
8
9
>>> from app.serializers import CustomerReportSerializer
>>> CustomerReportSerializer()
CustomerReportSerializer():
    id = IntegerField(label='ID', read_only=True)
    reference = CharField(max_length=20, validators=[<UniqueValidator(queryset=<QuerySet []>)>])
    time_raised = DateTimeField(read_only=True)
    description = CharField(required=True, style={'base_template': 'textarea.html'})
    class Meta:
        validators = [<UniqueTogetherValidator(queryset=CustomerReportRecord.objects.all(), fields=('reference', 'description'))>]

Now lets use only serializer to obtain unique together validation

1
2
3
4
5
6
7
8
9
10
11
12
13
from rest_framework import serializers
from rest_framework.validators import UniqueValidator, UniqueTogetherValidator
from .models import CustomerReportRecord


class CustomerReportSerializer(serializers.ModelSerializer):
    reference = serializers.CharField(max_length=20, validators=[UniqueValidator(queryset=CustomerReportRecord.objects.all())])

    class Meta:
        model = CustomerReportRecord
        fields = "__all__"
        validators = [UniqueTogetherValidator(queryset=CustomerReportRecord.objects.all(), fields=('reference',
                                                                                                   'description'))]
1
2
3
4
5
6
7
8
9
>>> from app.serializers import CustomerReportSerializer
>>> CustomerReportSerializer()
CustomerReportSerializer():
    id = IntegerField(label='ID', read_only=True)
    reference = CharField(max_length=20, validators=[<UniqueValidator(queryset=<QuerySet []>)>])
    time_raised = DateTimeField(read_only=True)
    description = CharField(style={'base_template': 'textarea.html'})
    class Meta:
        validators = [<UniqueTogetherValidator(queryset=<QuerySet []>, fields=('reference', 'description'))>]
1
2
3
4
5
6
7
8
from django.utils import timezone
from django.db import models


class CustomerReportRecord(models.Model):
    time_raised = models.DateTimeField(default=timezone.now, editable=False)
    reference = models.CharField(max_length=20)
    description = models.TextField()

Leave a comment