django-jsonform

repository·master·Indexed 19 days ago

https://github.com/bhch/django-jsonform

A user-friendly JSON editing form designed for the Django admin interface. It provides a React-based editor via JSONFormWidget, JSONFormField, and specialized model fields like JSONField and ArrayField. It supports complex data structures through schemas, including recursive nesting with $ref, autocomplete widgets with AJAX handlers, and dynamic schemas using callables.

Tokens
17.8K
Snippets
69
Records
83
Agent score
64%

What's inside django-jsonform

  1. Define reusable schemas with the $defs keyword

    master

    To keep schemas organized and maintainable, you can define common structures in a single location under a $defs object. Other parts of the schema can then reference these definitions using $ref (e.g., #/ $defs/definition_name).

    {
        'type': 'object',
        'properties': {
            'billing_address': {
                '$ref': '#/$defs/address'
            },
            'shipping_address': {
                '$ref': '#/$defs/address'
            }
        },
    
        '$defs': {
            'address': {
                'type': 'object',
                'properties': {
                    'street': { 'type': 'string' },
                    'city': { 'type': 'string' },
                    'state': { 'type': 'string' }
                }
            }
        }
    }
  2. Understand Coordinates in django-jsonform

    master

    In django-jsonform, Coordinates are strings used to locate a specific nested item within a data structure (like a list or a dictionary).

    A coordinate string represents a chain of keys and indices separated by the section sign (§).

    Example Mapping:

    • To access data[0]['age'], the coordinate is '0§age'.
    • To access data[0]['children'][0]['age'], the coordinate is '0§children§0§age'.

    Note: The section sign (§) is used as the separator instead of a hyphen to allow field names to contain hyphens without ambiguity.

    # Example data structure
    data = [
        {
            'name': 'Alice',
            'age': 30,
            'children': [{'name': 'Carl', 'age': 8}]
        }
    ]
    
    # Coordinate for Alice's age: '0§age'
    # Coordinate for Carl's age: '0§children§0§age'
  3. Create recursive/nested structures using $ref

    master

    You can create infinitely nested data structures (such as a menu with dropdown sub-menus) by using the $ref keyword within your schema. By setting '$ref': '#', you instruct the schema to reference itself recursively, allowing an object to contain a property that follows the same schema definition.

    # Schema for a menu with nested children
    {
        'type': 'list',
        'items': {
            'type': 'dict',
            'keys': {
                'label': {
                    'type': 'string'
                },
                'link': {
                    'type': 'string'
                },
                'new_tab': {
                    'type': 'boolean',
                    'title': 'Open in new tab'
                },
                'children': {
                    '$ref': '#'
                }
            }
        }
    }
  4. Reference schema parts using the $ref keyword

    master

    You can reuse parts of your schema by using the $ref keyword. This avoids duplication when multiple properties share the same structure. You can reference a property relative to the root using a JSON pointer (e.g., #/properties/name).

    {
        'type': 'object',
        'properties': {
            'billing_address': {
                'type': 'object',
                'properties': {
                    'street': { 'type': 'string' },
                    'city': { 'type': 'string' },
                    'state': { 'type': 'string' }
                }
            },
            'shipping_address': { '$ref': '#/properties/billing_address' }
        }
    }
  5. Configure input field types

    master

    The type keyword determines the base input field for a JSON schema. The following types are supported for input fields:

    • string: Used for text, email, date, file, and other specialized string inputs.
    • number: Used for numeric inputs, including floats.
    • integer: Used for integer-only numeric inputs.
    • boolean: Used for True/False inputs (renders as a checkbox by default).

    Note: array and object types do not have direct input fields themselves, but their children can.

    {
        'type': 'string'
    }
  6. Implement recursive nesting in schemas

    master

    The $ref keyword allows for recursive structures, such as a menu item that can contain a sub-menu of the same type. You can reference the current schema level using '$ref': '#'.

    Caution: Infinite Loops Be careful when using $ref to avoid infinite loops (e.g., object a references b, and b references a). If an infinite loop occurs, the widget will fail to render. If your widget is not rendering, check your browser's developer console for error messages.

    {
        'type': 'array',
        'title': 'Menu',
        'items': {
            'type': 'object',
            'properties': {
                'text': {
                    'type': 'string',
                    'title': 'Display text for the item'
                },
                'link': {
                    'type': 'string',
                    'title': 'URL of the item'
                },
                'children': { '$ref': '#' }
            }
        }
    }
  7. Use the 'file-url' format for uploading large files

    master

    To handle large files by saving them to the server and storing only a reference (path or URL) in the JSON data, use the file-url format in your schema. This is ideal for large files as it avoids embedding large Base64 strings directly in the JSON.

    In your schema, set the type to string and the format to 'file-url'.

    Example Schema:

    {
        'type': 'object',
        'keys': {
            'logo': {'type': 'string', 'format': 'file-url'}
        }
    }

    Example Output Data:

    {
        'logo': 'path/to/logo.png'
    }
    {
        'type': 'object',
        'keys': {
            'logo': {'type': 'string', 'format': 'file-url'}
        }
    }
  8. Handle datetime fields and timezone conversion

    master

    Using format: 'datetime' or format: 'date-time' (added in v2.8) saves values as ISO formatted strings (e.g., 2022-02-06T15:42:11.000+00:00).

    Timezone Conversion: When a user selects a time, the widget interprets it in the browser's local timezone and automatically converts it to UTC for database storage. The time picker uses a 12-hour format, but the stored value is converted to 24-hour format.

    {
        'type': 'string',
        'format': 'datetime'
    }
  9. Understand the django-jsonform Schema specification

    master

    django-jsonform uses a custom JSON Schema specification designed specifically for Django. While it is inspired by the standard JSON Schema spec (available at https://json-schema.org), it is not a 1:1 implementation.

    Key points to note:

    • It supports a subset of the standard JSON Schema features.
    • It includes additional custom features specifically for Django integration that are not part of the standard spec.
    • For complex implementations, refer to the project's examples or the validation guide.
  10. Format datetime in Django templates

    master

    Since datetime values are stored as ISO strings, use the parse_datetime template filter (added in v2.9) to convert them into Python datetime objects. This allows you to use Django's standard date filter.

    <!-- template.html -->
    {% load django_jsonform %}
    
    {{ date_string | parse_datetime }}
    
    <!-- Using with the date filter -->
    {{ date_string | parse_datetime | date:'d M, Y' }}
  11. Enable in-browser validation for JSON forms

    master

    You can enable client-side validation so that the JavaScript widget validates data before the form is submitted to the server. This only supports basic validation (schema keywords). Once passed, the data is still validated on the server by your custom rules.

    To enable this, set validate_on_submit = True on the widget.

    Option 1: In the Form's __init__ method

    class ShoppingListForm(forms.ModelForm):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.fields['items'].widget.validate_on_submit = True

    Option 2: In the Form's Meta class

    class ShoppingListForm(forms.ModelForm):
        class Meta:
            widgets = {
                'items': JSONFormWidget(schema=..., validate_on_submit=True)
            }
    # Option 1: In form's __init__ method
    class ShoppingListForm(forms.ModelForm):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.fields['items'].widget.validate_on_submit = True