Using Django REST Framework
With Django 1.8.4
and DRF 3.3.3
.
Here's a very simple custom JSONSchemaField class you can prop up using Django REST Framework and the jsonschema
package (available via pip install jsonschema
).
The custom field inherits from DRF's existing JSONField
class with some small changes. It add the step of validating incoming JSON against the JSONSchema definition. If the validation passes, the Django model TextField
is used to store/retrieve the raw JSON string.
In app/serializers.py
import json
from rest_framework import serializers
from jsonschema import validate # validates incoming data against JSONSchema
from jsonschema.exceptions import ValidationError as JSONSchemaValidationError
from .models import Lesson
from .jsonschema import (
notes_schema,
)
class JSONSchemaField(serializers.JSONField):
# Custom field that validates incoming data against JSONSchema,
# Then, if successful, will store it as a string.
def __init__(self, schema, *args, **kwargs):
super(JSONSchemaField, self).__init__(*args, **kwargs)
self.schema = schema
def to_representation(self, obj):
return json.loads(obj)
def to_internal_value(self, data):
try:
validate(data, self.schema)
except JSONSchemaValidationError as e:
raise serializers.ValidationError(e.message)
return super(JSONSchemaField, self).to_internal_value(json.dumps(data))
class LessonSerializer(serializers.HyperlinkedModelSerializer):
notes = JSONSchemaField(notes_schema)
class Meta:
model = Lesson
fields = ('url', 'title', 'bpm', 'notes')
In app/models.py
from django.db import models
class Lesson(models.Model):
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=True, default='Untitled')
bpm = models.DecimalField(max_digits=5, decimal_places=2, default=120.00)
notes = models.TextField()
class Meta:
ordering = ('created',)
In app/jsonschema.py
notes_schema = {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"pattern": "^[A-G][#b]?[0-9]$"
},
"duration": {
"type": "string",
"pattern": "^\d+\/\d+$"
}
},
"required": ["name", "duration"]
}
}
notes_example = [{"name": "C#4", "duration": "1/4"},
{"name": "A4", "duration": "1/32"}]
In app/views.py
from rest_framework import viewsets
from .models import Lesson
from .serializers import LessonSerializer
class LessonViewSet(viewsets.ModelViewSet):
queryset = Lesson.objects.all()
serializer_class = LessonSerializer