Skip to content

Commit

Permalink
Merge pull request #2 from maykinmedia/features/jsonschema-model
Browse files Browse the repository at this point in the history
Features/jsonschema model
  • Loading branch information
alextreme authored Jan 14, 2025
2 parents da29e49 + 34a9eb4 commit 37658b8
Show file tree
Hide file tree
Showing 12 changed files with 244 additions and 11 deletions.
23 changes: 19 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Welcome to django-json-schema's documentation!

|python-versions| |django-versions| |pypi-version|

<One liner describing the project>
A reusable Django app to store JSON schemas.

.. contents::

Expand All @@ -21,8 +21,10 @@ Welcome to django-json-schema's documentation!
Features
========

* ...
* ...
* JsonSchemaModel consisting of
- name CharField
- schema JsonField
- validate(json) method to validate JSON against the schema.

Installation
============
Expand All @@ -32,6 +34,7 @@ Requirements

* Python 3.10 or above
* Django 4.2 or newer
* A database supporting django.db.models.JSONField


Install
Expand All @@ -45,7 +48,19 @@ Install
Usage
=====

<document or refer to docs>
.. code-block:: python
from django_json_schema.models import JsonSchema
class ProductType(models.Model):
schema = models.ForeignKey(JsonSchema, on_delete=models.PROTECT)
class Product(models.Model):
json = models.JsonField()
type = models.ForeignKey(ProductType, on_delete=models.CASCADE)
def clean(self):
self.type.schema.validate(self.json)
Local development
=================
Expand Down
25 changes: 25 additions & 0 deletions django_json_schema/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import json

from django import forms
from django.contrib import admin

from .models import JsonSchema


class IndentedJSONEncoder(json.JSONEncoder):
def __init__(self, *args, indent, sort_keys, **kwargs):
super().__init__(*args, indent=2, **kwargs)


class JsonSchemaAdminForm(forms.ModelForm):
schema = forms.JSONField(encoder=IndentedJSONEncoder)

class Meta:
model = JsonSchema
fields = "__all__"


@admin.register(JsonSchema)
class JsonSchemaAdmin(admin.ModelAdmin):
form = JsonSchemaAdminForm
search_fields = ["name"]
43 changes: 43 additions & 0 deletions django_json_schema/locale/nl/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-07 05:36-0600\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#: models.py:10
msgid "name"
msgstr "naam"

#: models.py:10
msgid "Name of the json schema."
msgstr "Naam van het json schema."

#: models.py:14
msgid "schema"
msgstr ""

#: models.py:14
msgid "The schema that can be validated against."
msgstr "Het schema waartegen gevalideerd kan worden."

#: models.py:18
msgid "Json schema"
msgstr ""

#: models.py:19
msgid "Json Schemas"
msgstr "Json Schema's"
45 changes: 45 additions & 0 deletions django_json_schema/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Generated by Django 4.2.17 on 2025-01-03 16:37

from django.db import migrations, models


class Migration(migrations.Migration):
initial = True

dependencies = []

operations = [
migrations.CreateModel(
name="JsonSchema",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"name",
models.CharField(
help_text="Name of the json schema.",
max_length=200,
verbose_name="name",
),
),
(
"schema",
models.JSONField(
help_text="The schema that can be validated against.",
verbose_name="schema",
),
),
],
options={
"verbose_name": "Json schema",
"verbose_name_plural": "Json Schemas",
},
),
]
Empty file.
38 changes: 38 additions & 0 deletions django_json_schema/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _

from jsonschema import Draft202012Validator, validate
from jsonschema.exceptions import (
SchemaError,
ValidationError as JsonSchemaValidationError,
)


class JsonSchema(models.Model):
name = models.CharField(
_("name"), help_text=_("Name of the json schema."), max_length=200
)

schema = models.JSONField(
_("schema"), help_text=_("The schema that can be validated against.")
)

class Meta:
verbose_name = _("Json schema")
verbose_name_plural = _("Json Schemas")

def __str__(self):
return self.name

def clean(self):
try:
Draft202012Validator.check_schema(self.schema)
except SchemaError as e:
raise ValidationError(e.message)

def validate(self, json: dict) -> None:
try:
validate(json, self.schema, cls=Draft202012Validator)
except JsonSchemaValidationError as e:
raise ValidationError(e.message)
8 changes: 5 additions & 3 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ Welcome to django-json-schema's documentation!
..
|docs| |python-versions| |django-versions| |pypi-version|
<One liner describing the project>
A reusable Django app to store JSON schemas.

Features
========

* ...
* ...
* JsonSchemaModel consisting of
- name CharField
- schema JsonField
- validate(json) method to validate JSON against the schema.

.. toctree::
:maxdepth: 2
Expand Down
15 changes: 14 additions & 1 deletion docs/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,17 @@ Install from PyPI with pip:
Usage
=====

<document how to use the app here>
.. code-block:: python
from django.db import models
from django_json_schema.models import JsonSchema
class ProductType(models.Model):
schema = models.ForeignKey(JsonSchema, on_delete=models.PROTECT)
class Product(models.Model):
json = models.JsonField()
type = models.ForeignKey(ProductType, on_delete=models.CASCADE)
def clean(self):
self.type.schema.validate(self.json)
10 changes: 10 additions & 0 deletions manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
os.environ["DJANGO_SETTINGS_MODULE"] = "testapp.settings"

from django.core.management import execute_from_command_line

execute_from_command_line(sys.argv)
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ classifiers = [
]
requires-python = ">=3.10"
dependencies = [
"django>=4.2"
"django>=4.2",
"jsonschema>=4.23",
]

[project.urls]
Expand Down
2 changes: 0 additions & 2 deletions tests/test_dummy.py

This file was deleted.

43 changes: 43 additions & 0 deletions tests/test_json_schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from django.core.exceptions import ValidationError
from django.test import TestCase

from django_json_schema.models import JsonSchema


class TestJsonSchema(TestCase):
def setUp(self):
self.schema = JsonSchema.objects.create(
name="schema",
schema={
"type": "object",
"properties": {
"price": {"type": "number"},
"name": {"type": "string"},
},
"required": ["price", "name"],
},
)

def test_valid_json(self):
self.schema.validate(
{
"price": 10,
"name": "test",
}
)

def test_invalid_json(self):
with self.assertRaisesMessage(
ValidationError, "'price' is a required property"
):
self.schema.validate({"name": "Eggs"})

def test_clean_with_invalid_schema(self):
self.schema.schema = {"type": []}
with self.assertRaisesMessage(
ValidationError, "[] is not valid under any of the given schemas"
):
self.schema.clean()

def test_clean_with_valid_schema(self):
self.schema.clean()

0 comments on commit 37658b8

Please sign in to comment.