A modern, feature-rich rich text editor for Django.
Project description
Chedito
A modern, feature-rich rich text editor for Django using Quill.js.
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-pressedstate that updates when activated - Dropdown menus: Properly labeled with
aria-haspopup,aria-expanded, andaria-label - Dropdown options: Each option has a descriptive label (e.g., "Heading 1", "Normal text", "Align Center")
- Editor content area: Marked as
role="textbox"witharia-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:
- Installation
- Quick Start
- Configuration
- Model Fields
- Form Widgets
- Admin Integration
- Template Tags
- File Uploads
- Storage Backends
- Security
- API Reference
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
- Email: emmanuelasamoah179@gmail.com
- GitHub: @jasonpython50
Acknowledgments
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
719f1752f2ea09a8772cbb9cfb9d27761a80e48e4139a9e2127bf3188396137e
|
|
| MD5 |
6c570bce5082ab5a8594252462a53740
|
|
| BLAKE2b-256 |
0222c028dad16090698331f68c9adc27c03de8cdf5d8d9261887b44b2ca99a5b
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
66e7ccdb4e1a1354f3e985b584f40fe6d09bfe477d7229533e5513be1494654d
|
|
| MD5 |
d152d8c2634ee81a75622eddc8b39a11
|
|
| BLAKE2b-256 |
67f0bc00bcf905874133c45a307cce652c8261ce66f663af23c2b661157ef069
|