Rich-text fields for Django using Prosemirror - a powerful, schema-driven rich text editor.
Project description
1 django-prosemirror
- Version:
0.5.0
- Source:
- Keywords:
Django, Prosemirror, rich-text, editor, document, JSON, WYSIWYG, content editor, text editor, markdown, html
- PythonVersion:
3.11+
Rich-text fields for Django using Prosemirror - a powerful, schema-driven rich text editor.
2 Features
Rich-text editing: Full-featured Prosemirror editor integration with Django (admin) forms
Bidirectional conversion: Seamless conversion between HTML and ProseMirror document format
Configurable schemas: Fine-grained control over allowed content (headings, links, images, tables, etc.)
Native ProseMirror storage: Documents are stored in their native Prosemirror format, preserving structure and enabling programmatic manipulation without HTML parsing
3 Installation
3.1 Requirements
Python 3.11 or above
Django 4.2 or newer
3.2 Install
pip install maykin-django-prosemirror
Add to your Django settings:
INSTALLED_APPS = [
# ... your other apps
'django_prosemirror',
]
4 Usage
4.1 Model Field
Use ProseMirrorModelField in your Django models:
from django.db import models
from django_prosemirror.fields import ProseMirrorModelField
from django_prosemirror.schema import NodeType, MarkType
class BlogPost(models.Model):
title = models.CharField(max_length=200)
# Full-featured rich text content (uses default configuration allowing all node
# and mark types)
content = ProseMirrorModelField()
# Limited schema - only headings and paragraphs with bold text
summary = ProseMirrorModelField(
allowed_node_types=[NodeType.PARAGRAPH, NodeType.HEADING],
allowed_mark_types=[MarkType.STRONG],
null=True,
blank=True
)
# Default document
content_with_prompt = ProseMirrorModelField(
default=lambda: {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [{"type": "text", "text": "Start writing..."}]
}
]
}
)
4.2 Accessing Content
The field provides both document and HTML representations:
post = BlogPost.objects.get(pk=1)
# Access as ProseMirror document (dict)
doc_content = post.content.doc
# Output: {
# "type": "doc",
# "content": [
# {
# "type": "heading",
# "attrs": {"level": 1},
# "content": [{"type": "text", "text": "Heading"}]
# },
# {
# "type": "paragraph",
# "content": [
# {"type": "text", "text": "Paragraph content..."}
# ]
# }
# ]
# }
# Access as HTML
html_content = post.content.html
# Output: "<h1>Heading</h1><p>Paragraph content...</p>"
# Modify content from HTML, which will be converted to a Prosemirror document internally
post.content.html = "<h2>New heading</h2><p>Updated content</p>"
post.save()
# After modification, the document structure is updated
updated_doc = post.content.doc
# Output: {
# "type": "doc",
# "content": [
# {
# "type": "heading",
# "attrs": {"level": 2},
# "content": [{"type": "text", "text": "New heading"}]
# },
# {
# "type": "paragraph",
# "content": [{"type": "text", "text": "Updated content"}]
# }
# ]
# }
4.3 Form Field
Use ProsemirrorFormField in Django forms:
from django import forms
from django_prosemirror.fields import ProsemirrorFormField
from django_prosemirror.schema import NodeType, MarkType
class BlogPostForm(forms.Form):
title = forms.CharField(max_length=200)
# Full-featured editor (uses default configuration)
content = ProsemirrorFormField()
# Limited to headings and paragraphs with basic formatting
summary = ProsemirrorFormField(
allowed_node_types=[NodeType.PARAGRAPH, NodeType.HEADING],
allowed_mark_types=[MarkType.STRONG, MarkType.ITALIC],
required=False
)
4.4 Schema Configuration
Control exactly what content types are allowed using node and mark types:
from django_prosemirror.schema import NodeType, MarkType
# Available node types
NodeType.PARAGRAPH # Paragraphs (required)
NodeType.HEADING # Headings (h1-h6)
NodeType.BLOCKQUOTE # Quote blocks
NodeType.HORIZONTAL_RULE # Horizontal rules
NodeType.CODE_BLOCK # Code blocks
NodeType.FILER_IMAGE # Images (requires django-filer)
NodeType.HARD_BREAK # Line breaks
NodeType.BULLET_LIST # Bullet lists
NodeType.ORDERED_LIST # Numbered lists
NodeType.LIST_ITEM # List items
NodeType.TABLE # Tables
NodeType.TABLE_ROW # Table rows
NodeType.TABLE_CELL # Table data cells
NodeType.TABLE_HEADER # Table header cells
# Available mark types
MarkType.STRONG # Bold text
MarkType.ITALIC # Italic text (em)
MarkType.UNDERLINE # Underlined text
MarkType.STRIKETHROUGH # Strikethrough text
MarkType.CODE # Inline code
MarkType.LINK # Links
# Custom configurations
BASIC_FORMATTING = {
'allowed_node_types': [NodeType.PARAGRAPH, NodeType.HEADING],
'allowed_mark_types': [MarkType.STRONG, MarkType.ITALIC, MarkType.LINK],
}
BLOG_EDITOR = {
'allowed_node_types': [
NodeType.PARAGRAPH,
NodeType.HEADING,
NodeType.BLOCKQUOTE,
NodeType.IMAGE,
NodeType.BULLET_LIST,
NodeType.ORDERED_LIST,
NodeType.LIST_ITEM,
],
'allowed_mark_types': [
MarkType.STRONG,
MarkType.ITALIC,
MarkType.LINK,
MarkType.CODE,
],
}
TABLE_EDITOR = {
'allowed_node_types': [
NodeType.PARAGRAPH,
NodeType.HEADING,
NodeType.TABLE,
NodeType.TABLE_ROW,
NodeType.TABLE_CELL,
NodeType.TABLE_HEADER,
],
'allowed_mark_types': [MarkType.STRONG, MarkType.ITALIC],
}
# Use in fields
class DocumentModel(models.Model):
blog_content = ProseMirrorModelField(**BLOG_EDITOR)
table_content = ProseMirrorModelField(**TABLE_EDITOR)
4.5 Default Values
Always use callables for default values returning valid ProseMirror documents:
class Article(models.Model):
# ✅ Correct: Using a callable
content = ProseMirrorModelField(
default=lambda: {"type": "doc", "content": []}
)
# ❌ Wrong: Static dict (validation error)
# content = ProseMirrorModelField(
# default={"type": "doc", "content": []}
# )
4.6 Django Admin Integration
The field works automatically with Django admin:
from django.contrib import admin
from .models import BlogPost
@admin.register(BlogPost)
class BlogPostAdmin(admin.ModelAdmin):
fields = ['title', 'content', 'summary']
readonly_fields = ['summary'] # Read-only fields render as HTML
# Editable fields: Render the full ProseMirror rich-text editor
# Read-only fields: Render as formatted HTML output
4.7 Frontend Integration
Required Assets: The ProseMirror form fields require both CSS and JavaScript assets to function. These assets are mandatory for any template that renders ProseMirror form fields - without them, the rich text editor will not work.
{% load django_prosemirror %}
<!DOCTYPE html>
<html>
<head>
{% include_django_prosemirror_css %}
{% include_django_prosemirror_js_defer %}
</head>
<body>
{{ form.as_p }}
</body>
</html>
Note: These assets are only required for form rendering (editing). Displaying saved content using {{ post.content.html }} in templates does not require these assets.
5 Advanced Usage
Programmatic Content Creation
Create ProseMirror content programmatically:
# Create a document with heading and paragraph
content = {
"type": "doc",
"content": [
{
"type": "heading",
"attrs": {"level": 1},
"content": [{"type": "text", "text": "My Heading"}]
},
{
"type": "paragraph",
"content": [
{"type": "text", "text": "Some "},
{
"type": "text",
"marks": [{"type": "strong"}],
"text": "bold"
},
{"type": "text", "text": " text."}
]
}
]
}
article = Article.objects.create(content=content)
6 Local development
Requirements for development:
Node.js (for building frontend assets)
All runtime requirements listed above
Setup for development:
python -mvirtualenv .venv
source .venv/bin/activate
# Install Python package in development mode
pip install -e .[tests,coverage,docs,release]
# Install Node.js dependencies
npm install
# Build frontend assets (when making changes to JavaScript)
./build.sh
When running management commands via django-admin, make sure to add the root directory to the python path (or use python -m django <command>):
export PYTHONPATH=. DJANGO_SETTINGS_MODULE=testapp.settings
django-admin migrate
django-admin createsuperuser # optional
django-admin runserver
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 maykin_django_prosemirror-0.5.0.tar.gz.
File metadata
- Download URL: maykin_django_prosemirror-0.5.0.tar.gz
- Upload date:
- Size: 157.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
323bb088378e25e7eed402616921530930b6e38fa2ae931e295520b0bdab6dbd
|
|
| MD5 |
9c9a7269c1bb959973975477261d5a89
|
|
| BLAKE2b-256 |
836e3973b6752bce838e398a099d3c8b158ce94cdba36a0293ca8b64f1dee228
|
Provenance
The following attestation bundles were made for maykin_django_prosemirror-0.5.0.tar.gz:
Publisher:
ci.yml on maykinmedia/django-prosemirror
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
maykin_django_prosemirror-0.5.0.tar.gz -
Subject digest:
323bb088378e25e7eed402616921530930b6e38fa2ae931e295520b0bdab6dbd - Sigstore transparency entry: 1342578154
- Sigstore integration time:
-
Permalink:
maykinmedia/django-prosemirror@30632ee582269acf0da8fac69839da82aee7fe7e -
Branch / Tag:
refs/tags/0.5.0 - Owner: https://github.com/maykinmedia
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@30632ee582269acf0da8fac69839da82aee7fe7e -
Trigger Event:
push
-
Statement type:
File details
Details for the file maykin_django_prosemirror-0.5.0-py3-none-any.whl.
File metadata
- Download URL: maykin_django_prosemirror-0.5.0-py3-none-any.whl
- Upload date:
- Size: 152.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2135eb3980bf3748defb25f72c26402b9fb8ccbf75fe80d2c9a0b5903cf0863f
|
|
| MD5 |
3ca5ab7702f47678e3ac7600a474d145
|
|
| BLAKE2b-256 |
22fcdc84d368643eea35c46df23785bd2ec13c2073c0d28c51e6036c3e16dc63
|
Provenance
The following attestation bundles were made for maykin_django_prosemirror-0.5.0-py3-none-any.whl:
Publisher:
ci.yml on maykinmedia/django-prosemirror
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
maykin_django_prosemirror-0.5.0-py3-none-any.whl -
Subject digest:
2135eb3980bf3748defb25f72c26402b9fb8ccbf75fe80d2c9a0b5903cf0863f - Sigstore transparency entry: 1342578158
- Sigstore integration time:
-
Permalink:
maykinmedia/django-prosemirror@30632ee582269acf0da8fac69839da82aee7fe7e -
Branch / Tag:
refs/tags/0.5.0 - Owner: https://github.com/maykinmedia
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@30632ee582269acf0da8fac69839da82aee7fe7e -
Trigger Event:
push
-
Statement type: