Skip to main content

Overview

Django JSON Widget provides a user-friendly JSON editor with syntax highlighting for Django’s JSONField. Unfold automatically styles this widget with improved dark mode support and ensures seamless integration with your admin interface.
The widget makes editing JSON data much easier with features like syntax highlighting, automatic indentation, and validation.

Installation

1

Install django-json-widget

Install the package using pip:
pip install django-json-widget
2

Add to INSTALLED_APPS

Add django-json-widget to your settings:
settings.py
INSTALLED_APPS = [
    "unfold",
    "django.contrib.admin",
    # ...
    
    "django_json_widget",
]
No Unfold contrib app is needed. Unfold automatically applies custom CSS to style the JSON widget with proper dark mode support.

Basic Usage

Model with JSONField

models.py
from django.db import models

class Configuration(models.Model):
    name = models.CharField(max_length=200)
    settings = models.JSONField(default=dict)
    metadata = models.JSONField(default=dict, blank=True)
    
    def __str__(self):
        return self.name

Admin with JSON Widget

admin.py
from django.contrib import admin
from django import forms
from unfold.admin import ModelAdmin
from django_json_widget.widgets import JSONEditorWidget
from .models import Configuration

class ConfigurationAdminForm(forms.ModelForm):
    class Meta:
        model = Configuration
        fields = "__all__"
        widgets = {
            "settings": JSONEditorWidget,
            "metadata": JSONEditorWidget,
        }

@admin.register(Configuration)
class ConfigurationAdmin(ModelAdmin):
    form = ConfigurationAdminForm
    list_display = ["name"]

Widget Features

The JSON widget provides:
  • Syntax Highlighting: Color-coded JSON structure
  • Auto-Indentation: Properly formatted JSON
  • Validation: Real-time JSON syntax validation
  • Expand/Collapse: Collapse JSON sections for easier navigation
  • Dark Mode: Automatic theme switching with Unfold

Widget Options

Custom Options

Configure the widget behavior:
admin.py
from django_json_widget.widgets import JSONEditorWidget

class ConfigurationAdminForm(forms.ModelForm):
    class Meta:
        model = Configuration
        fields = "__all__"
        widgets = {
            "settings": JSONEditorWidget(
                options={
                    "mode": "code",  # 'code', 'tree', 'form', 'text', 'view'
                    "modes": ["code", "tree"],  # Available modes
                    "search": True,  # Enable search
                }
            ),
        }

Widget Modes

# Code mode (default) - Raw JSON with syntax highlighting
JSONEditorWidget(options={"mode": "code"})

# Tree mode - Interactive tree view
JSONEditorWidget(options={"mode": "tree"})

# Form mode - Form-based editing
JSONEditorWidget(options={"mode": "form"})

# View mode - Read-only display
JSONEditorWidget(options={"mode": "view"})

Multiple Modes

Allow users to switch between modes:
JSONEditorWidget(
    options={
        "mode": "code",
        "modes": ["code", "tree", "form", "view"],  # Available modes
    }
)

Common Use Cases

API Configuration

Store API settings as JSON:
models.py
class APIConfiguration(models.Model):
    name = models.CharField(max_length=200)
    config = models.JSONField(
        default=dict,
        help_text="API configuration in JSON format"
    )
    
    def __str__(self):
        return self.name
# Example JSON data
{
    "api_key": "your-api-key",
    "base_url": "https://api.example.com",
    "timeout": 30,
    "retry_attempts": 3,
    "endpoints": {
        "users": "/api/v1/users",
        "orders": "/api/v1/orders"
    }
}

Feature Flags

Manage feature flags:
models.py
class FeatureFlags(models.Model):
    environment = models.CharField(max_length=50)
    flags = models.JSONField(default=dict)
    
    class Meta:
        verbose_name_plural = "Feature flags"
# Example flags
{
    "new_dashboard": true,
    "beta_features": false,
    "maintenance_mode": false,
    "rate_limits": {
        "api": 1000,
        "login": 5
    }
}

Product Specifications

Store flexible product attributes:
models.py
class Product(models.Model):
    name = models.CharField(max_length=200)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    specifications = models.JSONField(
        default=dict,
        blank=True,
        help_text="Product specifications"
    )
# Example specifications
{
    "dimensions": {
        "width": 10,
        "height": 5,
        "depth": 3,
        "unit": "cm"
    },
    "weight": {
        "value": 250,
        "unit": "g"
    },
    "colors": ["black", "silver", "gold"],
    "features": [
        "Waterproof",
        "Wireless",
        "Rechargeable"
    ]
}

User Preferences

Store user settings:
models.py
from django.contrib.auth import get_user_model

class UserPreferences(models.Model):
    user = models.OneToOneField(
        get_user_model(),
        on_delete=models.CASCADE,
        related_name="preferences"
    )
    settings = models.JSONField(default=dict)
# Example preferences
{
    "theme": "dark",
    "language": "en",
    "notifications": {
        "email": true,
        "push": false,
        "sms": false
    },
    "privacy": {
        "profile_visible": true,
        "show_email": false
    }
}

Working with JSON Data

Creating Records

from myapp.models import Configuration

config = Configuration.objects.create(
    name="Production API",
    settings={
        "api_key": "secret-key",
        "timeout": 30,
        "retry": True,
    }
)

Updating JSON Fields

config = Configuration.objects.get(id=1)

# Update entire field
config.settings = {
    "api_key": "new-key",
    "timeout": 60,
}
config.save()

# Update specific key
config.settings["timeout"] = 45
config.save()

Querying JSON Fields

# Exact match
Configuration.objects.filter(
    settings={"timeout": 30}
)

# Key exists
Configuration.objects.filter(
    settings__has_key="api_key"
)

# Nested lookup
Configuration.objects.filter(
    settings__timeout=30
)

# Contains
Configuration.objects.filter(
    settings__contains={"retry": True}
)

Advanced Admin Configuration

Multiple JSON Fields

admin.py
class ConfigurationAdminForm(forms.ModelForm):
    class Meta:
        model = Configuration
        fields = "__all__"
        widgets = {
            "settings": JSONEditorWidget(
                options={"mode": "tree"}
            ),
            "metadata": JSONEditorWidget(
                options={"mode": "code"}
            ),
        }

@admin.register(Configuration)
class ConfigurationAdmin(ModelAdmin):
    form = ConfigurationAdminForm
    
    fieldsets = [
        ("Basic Information", {
            "fields": ["name"],
        }),
        ("Settings", {
            "fields": ["settings"],
        }),
        ("Metadata", {
            "fields": ["metadata"],
            "classes": ["collapse"],
        }),
    ]

Custom Validation

forms.py
from django import forms
from django.core.exceptions import ValidationError
import json

class ConfigurationAdminForm(forms.ModelForm):
    def clean_settings(self):
        settings = self.cleaned_data["settings"]
        
        # Validate required keys
        required_keys = ["api_key", "base_url"]
        for key in required_keys:
            if key not in settings:
                raise ValidationError(
                    f"Missing required key: {key}"
                )
        
        # Validate value types
        if not isinstance(settings.get("timeout"), (int, float)):
            raise ValidationError(
                "Timeout must be a number"
            )
        
        return settings

Read-Only JSON Display

admin.py
@admin.register(Configuration)
class ConfigurationAdmin(ModelAdmin):
    readonly_fields = ["formatted_settings"]
    
    def formatted_settings(self, obj):
        import json
        from django.utils.html import format_html
        
        formatted = json.dumps(obj.settings, indent=2)
        return format_html(
            "<pre>{}</pre>",
            formatted
        )
    formatted_settings.short_description = "Settings (Read-Only)"

Styling Integration

Unfold enhances the JSON widget with:
  • Dark Mode Support: Automatic theme switching
  • Consistent Colors: Matches Unfold’s color scheme
  • Proper Spacing: Aligned with Unfold’s design
  • Border Styling: Consistent with other form fields
The widget automatically inherits Unfold’s dark mode settings, providing a seamless experience when switching themes.

Performance Considerations

Large JSON Objects

For very large JSON objects, consider:
# Use 'view' mode for read-only display
JSONEditorWidget(options={"mode": "view"})

# Or limit editing modes
JSONEditorWidget(
    options={
        "mode": "code",
        "modes": ["code", "view"],  # Exclude tree/form modes
    }
)

Database Indexing

Add GIN indexes for JSON queries (PostgreSQL):
models.py
from django.contrib.postgres.indexes import GinIndex

class Configuration(models.Model):
    settings = models.JSONField(default=dict)
    
    class Meta:
        indexes = [
            GinIndex(fields=["settings"]),
        ]

Alternative Widgets

If you need different JSON editing experiences:
admin.py
# Use Unfold's WYSIWYG for rich text in JSON
from unfold.widgets import WysiwygWidget

# Or custom textarea
from django.forms import Textarea

widgets = {
    "settings": Textarea(attrs={"rows": 10}),
}

Resources

Django JSON Widget

Official package documentation

JSONField Documentation

Django’s JSONField reference
Always validate JSON data on the backend. Client-side validation in the widget is helpful but not sufficient for security.

Build docs developers (and LLMs) love