Skip to main content
https://badge.fury.io/py/django-reactive.svg https://github.com/tyomo4ka/django-reactive/workflows/CI/badge.svg?branch=master https://codecov.io/gh/tyomo4ka/django-reactive/branch/master/graph/badge.svg

django-reactive integrates react-jsonschema-form (RJSF) in Django projects.

Motivation

JSON types in Postgres allow combining both relational and non-relational approaches to storing data. That can lead to a simpler database design in the most cases.

Django provides ORM support for JSON types in Postgres and other databases via the JSONField model field. Also the JSONField form field allows basic support of JSON in forms. Django ORM even allows querying against the data stored inside the JSON structures. Moreover, it is possible to improve performance of these queries using GIN indexes with jsonb types in Django, which makes opens up a wide range of possibilities for simplifying application design, such as polymorphic collections, storing complex hierarchies in JSON, lists of related entities, etc.

However, the main limitation of JSONField in Django is the lack of good support of UI for JSON structures as defining JSON objects inside the textarea inputs is not practical for most use cases. django-reactive tries to address this problem by offering an integration between JSONField and the awesome react-jsonschema-form (RJSF) JavaScript library.

django-reactive also uses Python jsonschema <https://github.com/Julian/jsonschema> library for backend validation. Such integration can significantly reduce the amount of work needed for building custom forms for JSONField types.

In most cases it only requires a JSON schema configuration for such field and optionally a UI schema to modify some representation parameters.

A basic example of this is demonstrated below:

from django.db import models

from django_reactive.fields import ReactJSONSchemaField


class Registration(models.Model):
    basic = ReactJSONSchemaField(
        help_text="Registration form",
        schema={
            "title": "Register now!",
            "description": "Fill out the form to register.",
            "type": "object",
            "required": [
                "firstName",
                "lastName"
            ],
            "properties": {
                "firstName": {
                    "type": "string",
                    "title": "First name"
                },
                "lastName": {
                    "type": "string",
                    "title": "Last name"
                },
                "age": {
                    "type": "integer",
                    "title": "Age"
                },
                "bio": {
                    "type": "string",
                    "title": "Bio"
                },
                "password": {
                    "type": "string",
                    "title": "Password",
                    "minLength": 3
                },
                "telephone": {
                    "type": "string",
                    "title": "Telephone",
                    "minLength": 10
                }
            }
        },
        ui_schema={
            "firstName": {
                "ui:autofocus": True,
                "ui:emptyValue": ""
            },
            "age": {
                "ui:widget": "updown",
                "ui:title": "Age of person",
                "ui:description": "(earthian year)"
            },
            "bio": {
                "ui:widget": "textarea"
            },
            "password": {
                "ui:widget": "password",
                "ui:help": "Hint: Make it strong!"
            },
            "date": {
                "ui:widget": "alt-datetime"
            },
            "telephone": {
                "ui:options": {
                    "inputType": "tel"
                }
            }
        },
    )

It will generate a form like this:

images/simple.png

Quick start

Install django-reactive:

pip install django-reactive

Add it to your INSTALLED_APPS:

INSTALLED_APPS = (
    ...
    'django_reactive',
    ...
)

Running the example

Build the docker image for the Django application in example/:

  • Run docker compose up -d

This will automatically create the database, run migrations, import the default superuser, and run the Django development server on http://127.0.0.1:8000.

Django admin example

  • Open http://127.0.0.1:8000/admin/ and login with username admin and password test.

  • Go to the “Test models” admin section to see the example forms.

Normal Django view example

You will be redirected to the detail view of the created object after the form saves.

Usage outside of Django admin

To use outside of the Django admin, the following are required in the template:

  • A call to the form media property using {{ form.media }}

  • An HTML submit input with name=”_save”.

<!DOCTYPE html>
<html>
<head>
  <title>Homepage</title>
</head>
<body>
  {{ form.media }}
  <form method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="Save" name="_save">
  </form>
</body>
</html>

Optional configuration

Schema fields accept the following parameters for additional configuration:

  • extra_css: Include additional static CSS files available in the widget.

  • extra_js: Include additional static JavaScript files available in the widget.

  • on_render: A python method to make dynamic schema modifications at render-time.

Extra CSS and JSS files should be accessible using Django’s staticfiles configurations and passed as a list of strings.

Render methods require both schema and ui_schema as arguments to allow dynamic schema modification when rendering the widget. An optional instance keyword argument may also be used for referencing an object instance (must be set on the widget in the form). This method does not return anything.

Example usage

The example below demonstrates a use-case in which the options available for a particular field may be dynamic and unavailable in the initial schema definition. These would be populated at render-time and made available in the form UI.

def set_task_types(schema, ui_schema):
    from todos.models import TaskType

    task_types = list(TaskType.objects.all().values_list("name", flat=True))
    schema["definitions"]["Task"]["properties"]["task_type"]["enum"] = task_types
    ui_schema["task_lists"]["items"]["tasks"]["items"]["task_type"][
        "ui:help"
    ] = f"Select 1 of {len(task_types)} task types"

class Todo(models.Model):
    """
    A collection of task lists for a todo.
    """

    name = models.CharField(max_length=255)
    task_lists = ReactJSONSchemaField(
        help_text="Task lists",
        schema=TODO_SCHEMA,
        ui_schema=TODO_UI_SCHEMA,
        on_render=set_task_types,
        extra_css=["css/extra.css"],
        extra_js=["js/extra.js"],
    )

Schema model form class

The form class ReactJSONSchemaModelForm (subclassed from Django’s ModelForm) can be used to provide the model form’s instance object to the schema field widgets:

from django_reactive.forms import ReactJSONSchemaModelForm
class MyModelForm(ReactJSONSchemaModelForm):
    ...

This allows the on_render method set for a schema field to reference the instance like this:

def update_the_schema(schema, ui_schema, instance=None):
    if instance and instance.some_condition:
        ui_schema["my_schema_prop"]["ui:help"] = "Some extra help text"

Features

  • React, RJSF and other JS assets are bundled with the package.

  • Integration with default Django admin theme.

  • Backend and frontend validation.

  • Configurable static media assets.

  • Dynamic schema mutation in widget renders.

Limitations

To implement this behavior you can define an array schema with one property serving as a key of the object and do transformation in the Django form.

  • An outdated version (1.8) of RJSF is used in this project. Not all features of RJSF 1.8 are compatible with JSON Schema 4.0. Please, refer to the documentation if any issues.

Future development

  • At the moment there is no plans to add new features or support a newer version of RJSF.

  • Probably, it is a good idea to replace RJSF with a more Django-friendly solution. It would require significant development effort though, that’s why the idea is put on back burner at the moment.

Metadata

Release files for django-reactive 0.0.13

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-reactive 0.0.13
File Size Uploaded
django_reactive-0.0.13.tar.gz 357.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-reactive 0.0.13
File Interpreter ABI Platform
django_reactive-0.0.13-py3-none-any.whl Python 3 none any Details

Total release size: 719.0 kB

Release files / django_reactive-0.0.13.tar.gz

Download URL django_reactive-0.0.13.tar.gz
Size 357.8 kB
Tags Source
SHA-256 checksum
How to use checksums
0962716a8b08e739964a0aa6b34637e21529cb49e4fdc6c5b182e2e5decaf850
BLAKE2b-256 checksum
How to use checksums
03ec16e80f5e6f3730d4e458eca22e6121ff5911d875abbcad6f414d1983e821
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 27, 2026.

Transparency log

Release files / django_reactive-0.0.13-py3-none-any.whl

Download URL django_reactive-0.0.13-py3-none-any.whl
Size 361.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3918a592905645907ba9341f81ebbf204cdc3456b7cc290660604c68db01288f
BLAKE2b-256 checksum
How to use checksums
fce1718d6a0852c348a723f3953afcaaf45a34a13eb705c52287e9710fa71a3f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 27, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.0.13 This release

2 release files

0.0.12

2 release files

0.0.8

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.2

1 release file

0.0.1

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page