Skip to main content

A modern, feature-rich rich text editor for Django.

Project description

Chedito

A modern, feature-rich rich text editor for Django using Quill.js.

PyPI version Python versions Django versions License: MIT

Features

  • Modern Editor: Built on Quill.js - a powerful, free, open-source WYSIWYG editor
  • Full Media Support: Upload and embed images, videos, and file attachments
  • Drag & Drop: Drag and drop images and files directly into the editor
  • Paste Support: Paste images from clipboard
  • Django Admin Integration: Seamless integration with Django admin
  • Multiple Storage Backends: Use Django's default storage or custom backends
  • XSS Protection: Built-in HTML sanitization
  • Customizable: Fully configurable toolbar and editor options
  • Responsive: Works great on desktop and mobile devices
  • Accessible: Full screen reader support with ARIA labels for all toolbar controls
  • Offline Support: Quill.js is bundled locally - no CDN dependency
  • MIT Licensed: Free for personal and commercial use

Installation

pip install chedito

For HTML sanitization (recommended):

pip install chedito[sanitize]  # Uses nh3 (fast, Rust-based)
# or
pip install chedito[bleach]    # Uses bleach

Quick Start

1. Add to INSTALLED_APPS

# settings.py
INSTALLED_APPS = [
    ...
    'chedito',
]

2. Include URLs

# urls.py
from django.urls import path, include

urlpatterns = [
    ...
    path('chedito/', include('chedito.urls')),
]

3. Use in Models

# models.py
from django.db import models
from chedito.fields import RichTextField

class Article(models.Model):
    title = models.CharField(max_length=200)
    content = RichTextField()

4. Use in Admin

# admin.py
from django.contrib import admin
from chedito.admin import RichTextAdminMixin
from .models import Article

@admin.register(Article)
class ArticleAdmin(RichTextAdminMixin, admin.ModelAdmin):
    list_display = ['title']

5. Display in Templates

{% load chedito_tags %}

<!DOCTYPE html>
<html>
<head>
    {% chedito_css %}
</head>
<body>
    <article>
        {% render_rich_text article.content %}
    </article>
</body>
</html>

Configuration

Configure Chedito in your Django settings:

# settings.py
CHEDITO_CONFIG = {
    # Upload settings
    'upload_path': 'chedito_uploads/',
    'storage_backend': 'chedito.storage.default.DefaultStorage',

    # Size limits
    'max_image_size': 5 * 1024 * 1024,  # 5MB
    'max_video_size': 50 * 1024 * 1024,  # 50MB
    'max_file_size': 10 * 1024 * 1024,  # 10MB

    # Allowed file types
    'allowed_image_types': ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
    'allowed_video_types': ['video/mp4', 'video/webm'],

    # Security
    'require_authentication': False,
    'staff_only_uploads': False,
    'sanitize_html': True,

    # Editor settings
    'quill_theme': 'snow',  # 'snow' or 'bubble'
    'widget_height': '300px',

    # Quill configuration
    'quill_config': {
        'modules': {
            'toolbar': [
                [{'header': [1, 2, 3, False]}],
                ['bold', 'italic', 'underline', 'strike'],
                [{'color': []}, {'background': []}],
                ['blockquote', 'code-block'],
                [{'list': 'ordered'}, {'list': 'bullet'}],
                ['link', 'image', 'video'],
                ['clean'],
            ]
        },
        'placeholder': 'Write something...',
    },
}

Usage

Model Field

from chedito.fields import RichTextField

class Article(models.Model):
    # Basic usage
    content = RichTextField()

    # With custom configuration
    content = RichTextField(
        quill_config={
            'modules': {
                'toolbar': ['bold', 'italic', 'link']
            }
        }
    )

Form Widget

from django import forms
from chedito.widgets import RichTextWidget

class ArticleForm(forms.Form):
    content = forms.CharField(widget=RichTextWidget())

Form Field

from django import forms
from chedito.forms import RichTextFormField

class ArticleForm(forms.Form):
    content = RichTextFormField()

Template Tags

{% load chedito_tags %}

<!-- Include CSS (in <head>) -->
{% chedito_css %}

<!-- Include JS (before </body>) -->
{% chedito_js %}

<!-- Render rich text content -->
{% render_rich_text article.content %}

<!-- As a filter -->
{{ article.content|richtext }}

<!-- Strip HTML tags -->
{{ article.content|strip_tags }}

<!-- Truncate with ellipsis -->
{{ article.content|truncate_richtext:200 }}

Standalone Editor

{% load chedito_tags %}

{% chedito_editor "content" initial_value %}

Storage Backends

Django Default Storage (default)

Uses Django's configured default storage backend:

CHEDITO_CONFIG = {
    'storage_backend': 'chedito.storage.default.DefaultStorage',
}

Local Filesystem

CHEDITO_CONFIG = {
    'storage_backend': 'chedito.storage.local.LocalStorage',
}

Custom Backend

Create your own storage backend:

from chedito.storage.base import BaseStorage

class MyCustomStorage(BaseStorage):
    def save(self, file, filename, upload_type='file'):
        # Save file and return URL
        pass

    def delete(self, filename):
        # Delete file
        pass

    def url(self, filename):
        # Return file URL
        pass

    def exists(self, filename):
        # Check if file exists
        pass

Accessibility

Chedito is built with accessibility in mind and provides full screen reader support:

Screen Reader Support

All toolbar controls are properly labeled for screen readers (NVDA, JAWS, VoiceOver):

  • Toolbar buttons: Each button has descriptive aria-label (e.g., "Bold", "Italic", "Insert Image")
  • Toggle buttons: Include aria-pressed state that updates when activated
  • Dropdown menus: Properly labeled with aria-haspopup, aria-expanded, and aria-label
  • Dropdown options: Each option has a descriptive label (e.g., "Heading 1", "Normal text", "Align Center")
  • Editor content area: Marked as role="textbox" with aria-multiline="true"
  • Toolbar container: Has role="toolbar" with descriptive label

Keyboard Navigation

  • Tab through toolbar controls
  • Enter/Space to activate buttons
  • Arrow or tab keys to navigate dropdown options

Labels for All Controls

Control Label
Bold "Bold"
Italic "Italic"
Underline "Underline"
Strikethrough "Strikethrough"
Subscript "Subscript"
Superscript "Superscript"
Block Quote "Block Quote"
Code Block "Code Block"
Numbered List "Numbered List"
Bulleted List "Bulleted List"
Decrease Indent "Decrease Indent"
Increase Indent "Increase Indent"
Align buttons "Align Left", "Align Center", "Align Right", "Justify"
Link "Insert Link"
Image "Insert Image"
Video "Insert Video"
Clean "Remove Formatting"
Heading dropdown "Heading Style dropdown"
Color picker "Text Color dropdown"
Background picker "Background Color dropdown"

Security

HTML Sanitization

Chedito sanitizes HTML content to prevent XSS attacks. Install a sanitization library:

pip install chedito[sanitize]  # Recommended: uses nh3

Configure allowed tags and attributes:

CHEDITO_CONFIG = {
    'sanitize_html': True,
    'allowed_tags': ['p', 'br', 'strong', 'em', 'a', 'img', ...],
    'allowed_attributes': {
        'a': ['href', 'title'],
        'img': ['src', 'alt'],
        ...
    },
}

Upload Restrictions

CHEDITO_CONFIG = {
    'require_authentication': True,  # Require logged-in users
    'staff_only_uploads': True,      # Restrict to staff users
}

Admin Integration

Basic Integration

from chedito.admin import RichTextAdminMixin

@admin.register(Article)
class ArticleAdmin(RichTextAdminMixin, admin.ModelAdmin):
    pass

With Inlines

from chedito.admin import RichTextStackedInline, RichTextTabularInline

class CommentInline(RichTextStackedInline):
    model = Comment
    extra = 1

@admin.register(Article)
class ArticleAdmin(RichTextAdminMixin, admin.ModelAdmin):
    inlines = [CommentInline]

Custom Admin Configuration

@admin.register(Article)
class ArticleAdmin(RichTextAdminMixin, admin.ModelAdmin):
    chedito_config = {
        'modules': {
            'toolbar': ['bold', 'italic', 'link']
        }
    }

Requirements

  • Python 3.9+
  • Django 4.0+

Documentation

Full documentation is available in the docs directory:

Contributing

Contributions are welcome! Please read our contributing guidelines before submitting a pull request.

License

MIT License - Copyright (c) 2025 Emmanuel Asamoah

See LICENSE for details.

Author

Emmanuel Asamoah

Acknowledgments

  • Quill.js - The powerful rich text editor
  • Django - The web framework for perfectionists with deadlines

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

chedito-25.0.0.tar.gz (87.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

chedito-25.0.0-py3-none-any.whl (90.4 kB view details)

Uploaded Python 3

File details

Details for the file chedito-25.0.0.tar.gz.

File metadata

  • Download URL: chedito-25.0.0.tar.gz
  • Upload date:
  • Size: 87.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.0

File hashes

Hashes for chedito-25.0.0.tar.gz
Algorithm Hash digest
SHA256 719f1752f2ea09a8772cbb9cfb9d27761a80e48e4139a9e2127bf3188396137e
MD5 6c570bce5082ab5a8594252462a53740
BLAKE2b-256 0222c028dad16090698331f68c9adc27c03de8cdf5d8d9261887b44b2ca99a5b

See more details on using hashes here.

File details

Details for the file chedito-25.0.0-py3-none-any.whl.

File metadata

  • Download URL: chedito-25.0.0-py3-none-any.whl
  • Upload date:
  • Size: 90.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.0

File hashes

Hashes for chedito-25.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 66e7ccdb4e1a1354f3e985b584f40fe6d09bfe477d7229533e5513be1494654d
MD5 d152d8c2634ee81a75622eddc8b39a11
BLAKE2b-256 67f0bc00bcf905874133c45a307cce652c8261ce66f663af23c2b661157ef069

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page