Django Markdownx is a Markdown editor built for Django. It enables raw editing, live preview and image uploads (stored locally in MEDIA
folder) with drag&drop functionality and auto tag insertion.
(using Bootstrap for layout and styling)
Template is highly customizable, so you can easily use i.e. Bootstrap to layout editor pane and preview pane side by side. Using multiple editors on one page is supported.
Side note: Just to keep it simple, all UI editing controls are unwelcome – this is Markdown editor not a web MS Word imitation.
-
Install
django-markdownx
package.pip install django-markdownx
-
Add
markdownx
to yourINSTALLED_APPS
.#settings.py INSTALLED_APPS = ( [...] 'markdownx', )
-
Add url pattern to your
urls.py
.#urls.py urlpatterns = [ [...] url(r'^markdownx/', include('markdownx.urls')), ]
-
Collect included
markdownx.js
andmarkdownx.css
(for django admin styling) to yourSTATIC_ROOT
folder.python manage.py collectstatic
-
...and don't forget to include jQuery in your html file.
<head> [...] <script src="//code.jquery.com/jquery-2.1.1.min.js"></script> </head>
#models.py
from markdownx.models import MarkdownxField
class MyModel(models.Model):
myfield = MarkdownxField()
...and then, include a form's required media in the template using {{ form.media }}
:
<form method="POST" action="">{% csrf_token %}
{{ form }}
</form>
{{ form.media }}
#forms.py
from markdownx.fields import MarkdownxFormField
class MyForm(forms.Form):
myfield = MarkdownxFormField()
...and then, include a form's required media in the template using {{ form.media }}
:
<form method="POST" action="">{% csrf_token %}
{{ form }}
</form>
{{ form.media }}
When using included MarkdowxModel
class in your models, just use MarkdownxModelAdmin
as follows:
#admin.py
from django.contrib import admin
from markdownx.admin import MarkdownxModelAdmin
from .models import MyModel
admin.site.register(MyModel, MarkdownxModelAdmin)
However, when you want to use markdownx
with other classes – lets say TextField
– than override default widget as follows:
#admin.py
from django.db import models
from django.contrib import admin
from markdownx.widgets import AdminMarkdownxWidget
from .models import MyModel
class MyModelAdmin(admin.ModelAdmin):
formfield_overrides = {
models.TextField: {'widget': AdminMarkdownxWidget},
}
admin.site.register(MyModel, MyModelAdmin)
Place settings in your settings.py
to override default values:
#settings.py
# Markdownify
MARKDOWNX_MARKDOWNIFY_FUNCTION = 'markdownx.utils.markdownify' # Default function that compiles markdown using defined extensions. Using custom function can allow you to pre-process or post-process markdown text. See below for more info.
# Markdown extensions
MARKDOWNX_MARKDOWN_EXTENSIONS = [] # List of used markdown extensions. See below for more info.
MARKDOWNX_MARKDOWN_EXTENSION_CONFIGS = {} # Configuration object for used markdown extensions
# Markdown urls
MARKDOWNX_URLS_PATH = '/markdownx/markdownify/' # URL that returns compiled markdown text.
MARKDOWNX_UPLOAD_URLS_PATH = '/markdownx/upload/' # URL that accepts file uploads, returns markdown notation of the image.
# Media path
MARKDOWNX_MEDIA_PATH = 'markdownx/' # Path, where images will be stored in MEDIA_ROOT folder
# Image
MARKDOWNX_UPLOAD_MAX_SIZE = 52428800 # 50MB - maximum file size
MARKDOWNX_UPLOAD_CONTENT_TYPES = ['image/jpeg', 'image/png'] # Acceptable file content types
MARKDOWNX_IMAGE_MAX_SIZE = {'size': (500, 500), 'quality': 90,} # Different options describing final image processing: size, compression etc. See below for more info.
# Editor
MARKDOWNX_EDITOR_RESIZABLE = True # Update editor's height to inner content height while typing
Default function that compiles markdown looks like:
# utils.py
import markdown
from .settings import MARKDOWNX_MARKDOWN_EXTENSIONS, MARKDOWNX_MARKDOWN_EXTENSION_CONFIGS
def markdownify(content):
return markdown.markdown(content, extensions=MARKDOWNX_MARKDOWN_EXTENSIONS, extension_configs=MARKDOWNX_MARKDOWN_EXTENSION_CONFIGS)
#settings.py
MARKDOWNX_MARKDOWN_EXTENSIONS = [
'markdown.extensions.extra',
'markdown.extensions.nl2br',
'markdown.extensions.smarty',
]
Visit https://pythonhosted.org/Markdown/extensions/index.html to read more about markdown extensions.
Dict properties:
- size – (width, height). When
0
used, i.e.: (500,0), property will figure out proper height by itself - quality – default:
90
– image quality, from0
(full compression) to100
(no compression) - crop – default:
False
– ifTrue
, usesize
to crop final image - upscale – default:
False
– if image dimensions are smaller than those insize
, upscale image tosize
dimensions
Default widget's template looks like:
<div class="markdownx">
{{ markdownx_editor }}
<div class="markdownx-preview"></div>
</div>
When you want to use Bootstrap 3 and side-by-side panes (as in preview image above), just place markdownx/widget.html
file in your project's 'TEMPLATE_DIRS' folder with:
<div class="markdownx row">
<div class="col-md-6">
{{ markdownx_editor }}
</div>
<div class="col-md-6">
<div class="markdownx-preview"></div>
</div>
</div>
Markdown uses ![]()
syntax to insert uploaded image file. This generates very simple html <image>
tag. When you want to have more control and use your own html tags just create custom form_valid()
function in ImageUploadView
class.
Default ImageUploadView
class looks like:
#views.py
from django.http import JsonResponse
from django.views.generic.edit import FormView
from .forms import ImageForm
class ImageUploadView(FormView):
template_name = "dummy.html"
form_class = ImageForm
success_url = '/'
def form_invalid(self, form):
response = super(ImageUploadView, self).form_invalid(form)
if self.request.is_ajax():
return JsonResponse(form.errors, status=400)
else:
return response
def form_valid(self, form):
image_path = form.save()
response = super(ImageUploadView, self).form_valid(form)
if self.request.is_ajax():
image_code = '![]({})'.format(image_path)
return JsonResponse({'image_code': image_code})
else:
return response
Each markdownx jQuery object triggers two basic events:
- 'markdownx.init'
- 'markdownx.update' – also returns 'response' variable containing markdownified text
$('.markdownx').on('markdownx.init', function() {
console.log("INIT");
});
$('.markdownx').on('markdownx.update', function(e, response) {
console.log("UPDATE" + response);
});
- Markdown
- Pillow
- Django
- jQuery
- Support for Django's
default_storage
- Fix for missing MARKDOWNX_MARKDOWNIFY_FUNCTION in settings
- Possibility to customize image insertion code
- Markdown abstractable function fix
- Maintenance release
- Make rendering the markdown abstractable
- Added JS (jQuery) events
- Custom upload url path
- Fix when subclassing MarkdownxWidget
- Added Markdown extension configuration setting
- Fix by Eduard Sukharev: Fix accessing file length in python3
- Added custom url path setting MARKDOWNX_URLS_PATH to compile markdown with custom view (i.e. for pre/post altering markdown text)
- Setup tools fix
- Critical fix for image upload
- Package fix
- Python 3.3+ support
- Very simple test added just to test python 3 support
- Moved html logic from FormField to Widget to be able to override model objects other than included MarkdownxModel
- Fixed default value for
MARKDOWNX_EDITOR_RESIZABLE
- Warning: no backward compatibility
- Admin, Model and Form custom objects
- Django admin styles for compiled markdown
- Settings variables changed:
- MARKDOWNX_MAX_SIZE => MARKDOWNX_IMAGE_MAX_SIZE
- MARKDOWNX_MARKDOWN_KWARGS => MARKDOWNX_MARKDOWN_EXTENSIONS
- MARKDOWNX_MAX_UPLOADSIZE => MARKDOWNX_UPLOAD_MAX_SIZE
- MARKDOWNX_CONTENT_TYPES => MARKDOWNX_UPLOAD_CONTENT_TYPES
- Path fix by argaen
- Better editor height updates
- Refresh preview on image upload
- Small JS code fixes
- editor auto height
- JS event fix
- version bump
- Removed any inlcuded css
- Removed JS markdown compiler (full python support now with Markdown lib)
- Allow to paste tabs using Tab button
- package data fix
- README.md fix on PyPi
- critical setuptools fix
- change context name
editor
tomarkdownx_editor
for better consistency
- init
django-markdown is licensed under the open source BSD license. Read LICENSE
file for details.
It would be nice if anyone could support this project by adding missing functionality:
- tests
- JS intelligent auto-scrolling when side-by-side panes used
django-markdownx was inspired by great django-images and django-bootstrap-markdown packages.