Skip to main content
Latest Version https://github.com/jaap3/django-richtextfield/actions/workflows/ci.yml/badge.svg?branch=main

A Django model field and widget that renders a customizable rich text/WYSIWYG widget.

Works in Django’s admin interface and “normal” forms.

Supports global editor settings, reusable editor profiles and per field & widget settings. There’s built-in support for pluggable server side content sanitizers.

Tested with TinyMCE and CKEditor. Designed to be easily extended to use other editors.

Quickstart

Install django-richtextfield and add it to your Django project’s INSTALLED_APPS, django.contrib.admin must also be in INSTALLED_APPS:

INSTALLED_APPS = [
    'django.contrib.admin',
    ...
    'djrichtextfield'
]

Add the urls to the project’s urlpatterns:

path('djrichtextfield/', include('djrichtextfield.urls'))

Configure django-richtextfield in settings.py:

DJRICHTEXTFIELD_CONFIG = {
    'js': ['//cdn.tiny.cloud/1/no-api-key/tinymce/5/tinymce.min.js'],
    'init_template': 'djrichtextfield/init/tinymce.js',
    'settings': {
        'menubar': False,
        'plugins': 'link image',
        'toolbar': 'bold italic | link image | removeformat',
        'width': 700
    }
}

Now you’re ready to use the field in your models:

from djrichtextfield.models import RichTextField

class Post(models.Model):
    content = RichTextField()

or forms:

from djrichtextfield.widgets import RichTextWidget

class CommentForm(forms.ModelForm):
    content = forms.CharField(widget=RichTextWidget())

When using the editor outside of the admin make sure to include form.media in the <head> of the template:

<head>
  ...
  {{ form.media }}
  ...
</head>

Configuration

Define the DJRICHTEXTFIELD_CONFIG dictionary in your project settings. This dictionary can have the following keys:

Javascript souce(s)

'js'

A list of required javascript files. These can be URLs to a CDN or paths relative to your STATIC_URL e.g.:

'js': ['//cdn.ckeditor.com/4.14.0/standard/ckeditor.js']

or:

'js': ['path/to/editor.js', 'path/to/plugin.js']

CSS souce(s)

'css'

A dictionary of CSS files required. These can be URLs to a CDN or paths relative to your STATIC_URL e.g.:

'css': {
    'all': [
        'https://cdn.example.com/css/editor.css'
    ]
}

or:

'css': {'all': ['path/to/editor.css', 'path/to/plugin.css']}

Editor init template

'init_template'

Path to the init template for your editor. Currently django-richtextfield ships with two templates, either:

'init_template': 'djrichtextfield/init/tinymce.js'

or:

'init_template': 'djrichtextfield/init/ckeditor.js'

Editor settings

'settings'

A Python dictionary with the default configuration data for your editor e.g.:

'settings': {  # TinyMCE
    'menubar': False,
    'plugins': 'link image',
    'toolbar': 'bold italic | link image | removeformat',
    'width': 700
}

or:

'settings': {  # CKEditor
    'toolbar': [
        {'items': ['Format', '-', 'Bold', 'Italic', '-',
                   'RemoveFormat']},
        {'items': ['Link', 'Unlink', 'Image', 'Table']},
        {'items': ['Source']}
    ],
    'format_tags': 'p;h1;h2;h3',
    'width': 700
}

Editor profiles

'profiles'

This is an optional configuration key. Profiles are “named” custom settings used to configure specific type of fields. You can configure profiles like this:

'profiles': {
    'basic': {
        'toolbar': 'bold italic | removeformat'
    },
    'advanced': {
        'plugins': 'link image table code',
        'toolbar': 'formatselect | bold italic | removeformat |'
                   ' link unlink image table | code'
    }
}

Content sanitizers

'sanitizer'

This is an optional configuration key. A sanitizer can be used to process submitted values before it is returned by the widget. By default no processing is performed on submitted values. You can configure a sanitizer either by providing a function or an importable path to a function, like so:

'sanitizer': lambda value: '<h1>Title</h1>' + value

or:

'sanitizer': 'bleach.clean'
'sanitizer_profiles'

This is an optional configuration key. It is possible to override the default or configured sanitizer for each of the configured profiles. For example to set a custom sanitizer for the advanced profile:

'sanitizer_profiles': {
    'advanced': lambda value: value + 'This text has been sanitized.'
}

Field & Widget settings

You can override the default settings per field:

class CommentForm(forms.ModelForm):
    content = forms.CharField(widget=RichTextWidget())
    content.widget.field_settings = {'your': 'custom', 'settings': True}

or:

class Post(models.Model):
    content = RichTextField(
        field_settings={'your': 'custom', 'settings': True},
        sanitizer='bleach.linkify'
    )

It’s recommended to use profiles, they make it easier to switch configs or even editors on a later date. You use a profile like this:

class CommentForm(forms.ModelForm):
    content = forms.CharField(widget=RichTextWidget(field_settings='basic'))

or:

class Post(models.Model):
    content = RichTextField(field_settings='advanced')

Custom init / Using another editor

It should be fairly easy to use this project with another editor. All that’s required is to configure DJRICHTEXTFIELD_CONFIG to load the right Javascript/CSS files and to create a custom init template.

For example, to use jQuery based Summernote (lite) editor:

DJRICHTEXTFIELD_CONFIG = {
    'js': [
        '//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.js',
        '//cdnjs.cloudflare.com/ajax/libs/summernote/0.8.9/summernote-lite.js',
    ],
    'css': {
        'all': [
            '//cdnjs.cloudflare.com/ajax/libs/summernote/0.8.9/summernote-lite.css',
        ]
    },
    'init_template': 'path/to/init/summernote.js',
    'settings': {
        'followingToolbar': False,
        'minHeight': 250,
        'width': 700,
        'toolbar': [
            ['style', ['bold', 'italic', 'clear']],
        ],
    }
}

Init template

The init template is a Django template (so it should be in the template and not in the static directory). It contains a tiny bit of Javascript that’s called to initialize each editor. For example, the init template for Summernote would like this:

$('#' + id).summernote(settings)

The init template has the following Javascript variables available from the outer scope:

field

DOM node of the textarea to be replaced

id

The id attribute of the textarea

default_settings

DJRICHTEXTFIELD_CONFIG['settings'] as a JS object

custom_settings

The field_settings as a JS object

settings

Merge of default_settings and custom_settings

Handling uploads & other advanced features

django-richtextfield built to be editor agnostic. This means that it’s up to you to handle file uploads, show content previews and support other “advanced” features.

History

1.6.2 (2024-03-07)

  • Code refresh, no functional changes

  • Switch CI to GitHub actions

  • Test with Django 3.2 and 4.2

  • Test with Python 3.8, 3.10 and 3.12

  • Drop support for older Python and Django versions

1.6.1 (2021-04-14)

  • Support Django 3.2 (no changes necessary)

  • Test with Django main (4.0)

  • Use force_str instead of force_text, the latter is deprecated and removed in Django 4.0

1.6 (2020-05-20)

  • init.js no longer depends on jQuery. This might a backwards incompatible change for:

    • Users that have defined their own init script that dependeds on the $e var. This var has been replaced by field which is a plain DOM node instead of a jQuery object.

    • Users that depended on the implicit load of Django’s bundled version of jQuery now have explicitly load it themselves.

  • Verified TinyMCE init/config works with TinyMCE 4 and 5

  • Tested and verified to work with Django 3.1

1.5.0 (2019-12-04)

  • Drop support for Python 2

  • Drop support for Django < 2.2

  • Add support for Django 3.0

1.4.0 (2019-01-31)

  • NOTE: This is the final release that supports Python 2!

  • Add support for plugable server side content sanitizers

1.3.0 (2018-11-05)

  • Allow CSS files to be included by a RichTextWidget

1.2.4 (2018-09-25)

  • Fix display issue in Django 2.1’s admin interface

1.2.3 (2018-09-11)

  • Add support for Django 2.1

1.2.2 (2018-06-12)

  • Conditionally load the (un)minified version of jquery depending on DEBUG

  • Load jQuery before all other scripts

1.2.1 (2018-01-18)

  • Add ['admin/js/vendor/jquery/jquery.min.js', 'admin/js/jquery.init.js'] to RichTextWidget.media.js. This makes the widget usable outside of the admin (but still requires django.contrib.admin to be in INSTALLED_APPS) and prevents javascript errors inside the admin in certain edge cases.

1.2 (2017-12-04)

  • Remove support for Django < 1.11

  • Add support for Django 2.0

1.1 (2016-01-14)

  • Remove support for Django < 1.8

  • Tested with Django 1.8 & Django 1.9

1.0.1 (2014-11-13)

  • Fix unicode error

1.0 (2014-09-30)

  • First release

Metadata

Release files for django-richtextfield 1.6.2

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-richtextfield 1.6.2
File Size Uploaded Provenance
django-richtextfield-1.6.2.tar.gz 16.6 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for django-richtextfield 1.6.2
File Interpreter ABI Platform
django_richtextfield-1.6.2-py3-none-any.whl Python 3 none any Details

Total release size: 28.1 kB

Release files / django-richtextfield-1.6.2.tar.gz

Download URL django-richtextfield-1.6.2.tar.gz
Size 16.6 kB
Tags Source
SHA-256 checksum
How to use checksums
7efe9c397437922ed6ca3071b7a54db5bf8ebfa4ced7172dfb0b7472df66b0c4
BLAKE2b-256 checksum
How to use checksums
b81375336f29fc675192ed8a247025327d591898e9c7ac7df39952c2eab896b7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.0.0 CPython/3.8.12

Release files / django_richtextfield-1.6.2-py3-none-any.whl

Download URL django_richtextfield-1.6.2-py3-none-any.whl
Size 11.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
56b7404f52ae9a313bcbdb4c47aea16e5fa26a48539c1221e3de39186ee839bf
BLAKE2b-256 checksum
How to use checksums
4ddc96a1f25ff2270c807c0e3ad242ea96f40561ad10c066d1a1741367e188f4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.0.0 CPython/3.8.12

Security for django-richtextfield 1.6.2

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

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