Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • AlekSIS/libs/django-forms-as-jsonschema
1 result
Show changes
Commits on Source (6)
__version__ = '0.1.0'
from .forms import JSONSchemaFormMixin
from .jsonschema import JSONSchema
from .layout import Fieldset, Layout, LayoutNode, Row
__all__ = [JSONSchema, JSONSchemaFormMixin, Fieldset, Layout, Row]
......@@ -4,8 +4,13 @@ from .jsonschema import JSONSchema
class JSONSchemaFormMixin:
layout = None
def as_jsonschema(self: Form) -> dict:
schema = JSONSchema()
for name, field in self.fields.items():
schema.add_field(name, field)
if self.layout is None:
for name, field in self.fields.items():
schema.add_field(name, field)
else:
schema.update_properties(self.layout.build_schema(schema, self.fields)["properties"])
return schema.schema
from django import forms
from django_forms_as_jsonschema.layout import _Section
class JSONSchema:
......@@ -10,68 +12,14 @@ class JSONSchema:
"properties": {},
}
# example_json = {
# "type": "object",
# "properties": {
# "stringProp": {
# "type": "string",
# "title": "I'm a string",
# "description": "This description is used as a help message."
# },
# "stringTextareaProp": {
# "type": "string",
# "title": "I'm a string in a textarea",
# "x-display": "textarea"
# },
# "numberProp": {
# "type": "number",
# "title": "I'm a number"
# },
# "integerProp": {
# "type": "integer",
# "title": "I'm an integer"
# },
# "integerSliderProp": {
# "type": "integer",
# "title": "I'm an integer in a slider",
# "x-display": "slider",
# "minimum": 0,
# "maximum": 5
# },
# "booleanProp": {
# "type": "boolean",
# "title": "I'm a boolean",
# "description": "This description is used as a help message."
# },
# "booleanSwitchProp": {
# "type": "boolean",
# "title": "I'm a boolean with switch display",
# "x-display": "switch",
# "description": "This description is used as a help message."
# },
# "stringArrayProp": {
# "type": "array",
# "title": "I'm an array of strings",
# "items": {
# "type": "string"
# }
# },
# "integerArrayProp": {
# "type": "array",
# "title": "I'm an array of integers",
# "items": {
# "type": "integer"
# }
# }
# }
# }
def add_field(self, name, field):
@staticmethod
def generate_field(field):
new_field = {
"type": "string",
"title": str(field.label or ""),
"description": str(field.help_text or ""),
"readOnly": field.disabled
"readOnly": field.disabled,
"required": field.required,
}
# string, number, integer, boolean.
......@@ -162,6 +110,36 @@ class JSONSchema:
...
else:
print(field, type(field), type(field.widget))
print("[Django-forms-as-jsonschema] Unsupported field/widget detected: ")
print(f"{field=}, {type(field)=}, {type(field.widget)=}")
return new_field
def add_field(self, name, field, metadata: dict = None):
new_field = self.generate_field(field)
if metadata and metadata.get("section_name") and self.schema["properties"].get(
metadata["section_name"]
) is not None:
self.schema["properties"][metadata["section_name"]]["properties"] = \
self.schema["properties"][metadata["section_name"]]["properties"] or {}
self.schema["properties"][metadata["section_name"]]["properties"][name] = new_field
else:
self.schema["properties"][name] = new_field
@staticmethod
def generate_section(section: _Section):
sec = {
"title": section.title,
"type": "object",
"properties": {}
}
if section.description:
sec["description"] = section.description
return sec
def add_section(self, section: _Section):
self.schema["properties"][section.codename] = self.generate_section(section)
self.schema["properties"][name] = new_field
def update_properties(self, props: dict):
self.schema["properties"].update(props)
import uuid
from abc import ABC
from dataclasses import dataclass
from typing import Union
from django.utils.text import slugify
class LayoutNode(ABC):
def __init__(self, *elements):
self.elements = elements
def build_schema(self, schema, form_fields) -> dict:
props = {}
for field in self.elements:
if isinstance(field, LayoutNode):
built_schema = field.build_schema(schema, form_fields)
props.update(built_schema["properties"])
else:
props[field] = schema.generate_field(form_fields[field])
return {"properties": props}
@dataclass
class _Section:
codename: str
title: str
description: str = None
class Row(LayoutNode):
def build_schema(self, schema, form_fields):
row_name = "row-" + str(uuid.uuid4())
fields = super().build_schema(schema, form_fields)["properties"]
for k in fields.keys():
fields[k]["x-options"] = {
"fieldColProps": {
"cols": 12,
"md": ""
}
}
return {
"properties": {
row_name: {
"x-cols": 12,
"properties": fields
}
}
}
class Fieldset(LayoutNode):
def __init__(self, name: Union[tuple[str, str], str], *elements):
super().__init__(*elements)
codename = str(slugify(name))
if type(name) == tuple:
self.section = _Section(codename, *map(str, name))
else:
self.section = _Section(codename, str(name))
def build_schema(self, schema, form_fields) -> dict:
section = schema.generate_section(self.section)
section["properties"].update(super().build_schema(schema, form_fields)["properties"])
return {
"properties": {
self.section.codename: section
}
}
class Layout(LayoutNode):
...