Usage¶
Validating JSONField¶
Create a validator by passing a valid JSON schema to
JsonSchemaValidator
:
from jsonfield_validation import JsonSchemaValidator
max2 = JsonSchemaValidator({"maxItems": 2})
class MyModel(models.Model):
items = models.JSONField(validators=[max2])
As with any Django model validators, be sure to call clean_fields
if necessary:
instance = MyModel(items=[1, 2, 3])
instance.clean_fields()
django.core.exceptions.ValidationError: {'items': ["[1, 2, 3] is too long"]}
JSONField support¶
JsonSchemaValidator
is tested against the JSONField
implementation included in Django > 3.1, and
against django-jsonfield for prior versions of Django.
Generating JSON schemas with Pydantic¶
Pydantic is a great fit for Django JSON Field Validator:
from django.db import models
from pydantic import BaseModel
class Point(BaseModel):
x: int
y: int
class Points(BaseModel):
__root__: List[Point]
class Shape(models.Model):
points: models.JSONField(
validators=[JsonSchemaValidator(schema=Points.schema())]
)
Testing a schema against existing data¶
If you’re adding schema validation to a model with existing records, you may wish to verify that the existing data will match the proposed schema.
The JsonSchemaValidator
provides a check
method,
which will run schema validation without raising an exception.
This is ideal for validating many objects without the overhead
of exception handling.
The check
method will return an error dict if validation
fails, and None
if it succeeds. Errors are keyed by
the flattened path to the errant value. Nested keys are
concatenated with a .
by default. If the error occurs
in a list, the errant item’s position is noted with
square brackets, using standard 0-based indexing.
As an example, here’s a nested object containing a list that is meant to be all numbers, but a string snuck in:
data = {
"students": {
"Alice": {
"scores": [85, 92, "A"]
{
}
}
The key in the error dict for this would be "students.Alice.scores.[2]"
:
validator = JsonSchemaValidator(...)
validator.check(data)
{"students.Alice.scores.[2]": "'A' is not a number"}
A simple check against all records could then be performed like:
validator = JsonSchemaValidator({"maxItems": 2})
if any(validator.check(obj.json_field) for obj in MyModel.objects.iterator()):
print("Validation failed")
Of course, if validation does fail, you’ll probably want to know which object failed, and why. A more robust example:
validator = JsonSchemaValidator({"maxItems": 2})
error_map = {}
for obj in MyModel.objects.iterator():
errors = validator.check(obj)
if errors:
error_map[obj.id] = errors