This is a field that allows multiple markup types but also stores the pre-rendered result in the database which offers an advantage over calling one of the render methods each time. Example usage looks like:
class BlogPost(models.Model):
...
post = MarkupField()
the various extra fields can then be accessed as follows:
BlogPost.objects.get(pk=1).post # raw content
BlogPost.objects.get(pk=1).post_markup_type # markup type (plain text, html, markdown, rest, textile)
BlogPost.objects.get(pk=1).post_rendered # content of post rendered to html
BlogPost.objects.get(pk=1).post_as_html # property that access post_rendered but marked safe for easy use in templates
After writing my initial version of this I was pointed at the similar http://www.djangosnippets.org/snippets/1169/
I find mine a bit more useful as it includes ReST and includes a mark_safe call to allow showing the rendered HTML directly. I have however borrowed the nice idea of dynamically building MARKUP_TYPES from #1169.
Also available via http://gist.github.com/67724.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | # for suggestions please fork & improve @ http://gist.github.com/67724
from django.db import models
from django.conf import settings
from django.utils.html import linebreaks, urlize
from django.utils.safestring import mark_safe
PLAIN_TEXT, HTML, MARKDOWN, REST, TEXTILE = range(5)
MARKUP_TYPES = (
(PLAIN_TEXT, 'plain'),
(HTML, 'html'),
)
try:
import markdown
MARKUP_TYPES += ( (MARKDOWN, 'markdown'), )
except ImportError:
pass
try:
from docutils.core import publish_parts
MARKUP_TYPES += ( (REST, 'restructuredtext'), )
except ImportError:
pass
try:
import textile
MARKUP_TYPES += ( (TEXTILE, 'textile'), )
except ImportError:
pass
_rendered_field_name = lambda name: '%s_rendered' % name
_markup_type_field_name = lambda name: '%s_markup_type' % name
class MarkupField(models.TextField):
def __init__(self, *args, **kwargs):
super(MarkupField, self).__init__(*args, **kwargs)
def contribute_to_class(self, cls, name):
markup_type_field = models.PositiveIntegerField(choices=MARKUP_TYPES, default=PLAIN_TEXT)
rendered_field = models.TextField(editable=False)
cls.add_to_class(_markup_type_field_name(name), markup_type_field)
cls.add_to_class(_rendered_field_name(name), rendered_field)
super(MarkupField, self).contribute_to_class(cls, name)
def as_html(self):
return mark_safe(getattr(self, _rendered_field_name(name)))
cls.add_to_class('%s_as_html' % name, property(as_html))
def pre_save(self, model_instance, add):
markup_type = getattr(model_instance, _markup_type_field_name(self.attname))
markup = getattr(model_instance, self.attname)
if markup_type == MARKDOWN:
rendered = markdown.markdown(markup)
elif markup_type == REST:
docutils_settings = getattr(settings, "RESTRUCTUREDTEXT_FILTER_SETTINGS", {})
parts = publish_parts(source=markup, writer_name="html4css1", settings_overrides=docutils_settings)
rendered = parts["fragment"]
elif markup_type == TEXTILE:
rendered = textile.textile(markup, encoding='utf-8', output='utf-8')
elif markup_type == PLAIN_TEXT:
rendered = urlize(linebreaks(markup))
else:
rendered = markup
setattr(model_instance, _rendered_field_name(self.attname), rendered)
return markup
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 11 months ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 1 week ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Very nice snippet, thank you. I will be using it for sure. One thing I noticed. Is the below required? I think it can just be taken out.
#
I wasn't able to successfully run syncdb command (when using django-trunk) when I have defined more than one MarkupField in my models.
#
Please login first before commenting.