Documented by kbruder tech✞ᵀᵀᴹ on Jul 10, 2021
In order to show highlighted code content on your Django website, let us take advantage of existing code that people have made available. In this approach we will use the Python module “Pygments”, a nice solarized theme by Ethan Schoonover and the Code model from Lookaway CMS as an example. "Code" is a simple model with some meta data and a TextField that contains the code. With Pygments and theme handling the rendering for us and our Code model providing the text to be rendered, the question is, “when and where to render?”.
There are many ways to do this in Django, but we are going to create a custom template filter that will take a given string and return HTML that will render the highlighted text. This operation will be done every time the template itself is rendered, which is kind of a bummer, but this is kind of what Django is doing all the time anyway.
This example is straight out of the Lookaway CMS code. The Objects App features a Code model. The model has a user-supplied, optional “file_path” field which we will attempt to use in order to determine which lexer to use. If the model instance does not have a “file_path” then we will use Pygments’ guess_lexer() method.
objects/models.pyclass Code(MetaDataMixin, MarshmallowMixin):
title = models.CharField(
max_length=256,
)
text = models.TextField(
max_length=1024,
blank = True,
null = True,
)
code = models.TextField(
max_length=65535,
)
language = models.CharField(
max_length=64,
)
language_version = models.CharField(
max_length=64,
)
file_path = models.CharField(
max_length=256,
blank=True,
null=True,
)
source = models.CharField(
max_length=64,
blank=True,
null=True,
)
source_url = models.URLField(
max_length=256,
blank=True,
null=True,
)
md5 = models.CharField(
max_length=32,
)
class Meta:
ordering = ['order', 'title', 'language',]
def __str__(self):
return '{} - {} {}'.format(self.title, self.language, self.language_version,)
def get_absolute_url(self):
return reverse('objects:code_detail', kwargs={'pk': self.pk,})
def get_md5(string):
'''
Given a string, returns a MD5 hash digest
'''
md5 = hashlib.md5(string.encode())
digest = md5.hexdigest()
return digest
Source: Kbruder Tech
Ensure your app has a directory contains a “templatetags” directory that, in turn, contains the empty “__init__.py” file. Create or edit a custom filter python file in “templatetags” as well. Add the following lines in the filter file.
objects/templatetags/custom_filters.pyfrom django import template
register = template.Library()
## Objects
@register.filter(needs_autoescape=True)
def highlight_syntax(code, path, autoescape=True):
from pygments import highlight
from pygments.lexers import guess_lexer, get_lexer_for_filename
from pygments.formatters import HtmlFormatter
from django.utils.safestring import mark_safe
if path:
try:
lexer = get_lexer_for_filename(path)
except:
lexer = guess_lexer(code)
else:
lexer = guess_lexer(code)
formatter = HtmlFormatter(cssclass="highlight code-block", style="friendly")
return mark_safe(highlight(code, lexer, formatter))
Source: Kbruder Tech
objects/templatetags/
├── custom_filters.py
├── __init__.py
You app directory should resemble something like this.
If you are using Bootstrap, add the following CSS lines to your styles to resolve an issue with font coloring in <pre> tags inheriting in a way that causes the text to become unreadable. This issue took me a long time to nail down, but I did find a great article that sorted me out.
static/styles.css/* Thanks to this site for solving the Bootstrap/Pygments conflict */
/* https://alcidanalytics.com/p/pygments-bootstrap-css-conflict */
/* fix bootstrap conflict with Pygments */
.highlight pre { white-space: pre;
border-radius: inherit;
display: inherit;
background-color: inherit;
border: inherit;
color: inherit;
}
/* fix conflict with other sytlesheets */
.highlight .m { margin: inherit; }
Source: Alcid Analyitcs
Include the following lines with your CSS. There are many other themes available if this one doesn’t work with your existing style. Also, you can mess around with changing these colors to create your own theme.
static/styles.css/* Solarized Dark
For use with Jekyll and Pygments
http://ethanschoonover.com/solarized
SOLARIZED HEX ROLE
--------- -------- ------------------------------------------
base03 #002b36 background
base01 #586e75 comments / secondary content
base1 #93a1a1 body text / default code / primary content
orange #cb4b16 constants
red #dc322f regex, special keywords
blue #268bd2 reserved keywords
cyan #2aa198 strings, numbers
green #859900 operators, other keywords
*/
.highlight { background-color: #002b36; color: #93a1a1 }
.highlight .c { color: #586e75 } /* Comment */
.highlight .err { color: #93a1a1 } /* Error */
.highlight .g { color: #93a1a1 } /* Generic */
.highlight .k { color: #859900 } /* Keyword */
.highlight .l { color: #93a1a1 } /* Literal */
.highlight .n { color: #93a1a1 } /* Name */
.highlight .o { color: #859900 } /* Operator */
.highlight .x { color: #cb4b16 } /* Other */
.highlight .p { color: #93a1a1 } /* Punctuation */
.highlight .cm { color: #586e75 } /* Comment.Multiline */
.highlight .cp { color: #859900 } /* Comment.Preproc */
.highlight .c1 { color: #586e75 } /* Comment.Single */
.highlight .cs { color: #859900 } /* Comment.Special */
.highlight .gd { color: #2aa198 } /* Generic.Deleted */
.highlight .ge { color: #93a1a1; font-style: italic } /* Generic.Emph */
.highlight .gr { color: #dc322f } /* Generic.Error */
.highlight .gh { color: #cb4b16 } /* Generic.Heading */
.highlight .gi { color: #859900 } /* Generic.Inserted */
.highlight .go { color: #93a1a1 } /* Generic.Output */
.highlight .gp { color: #93a1a1 } /* Generic.Prompt */
.highlight .gs { color: #93a1a1; font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #cb4b16 } /* Generic.Subheading */
.highlight .gt { color: #93a1a1 } /* Generic.Traceback */
.highlight .kc { color: #cb4b16 } /* Keyword.Constant */
.highlight .kd { color: #268bd2 } /* Keyword.Declaration */
.highlight .kn { color: #859900 } /* Keyword.Namespace */
.highlight .kp { color: #859900 } /* Keyword.Pseudo */
.highlight .kr { color: #268bd2 } /* Keyword.Reserved */
.highlight .kt { color: #dc322f } /* Keyword.Type */
.highlight .ld { color: #93a1a1 } /* Literal.Date */
.highlight .m { color: #2aa198 } /* Literal.Number */
.highlight .s { color: #2aa198 } /* Literal.String */
.highlight .na { color: #93a1a1 } /* Name.Attribute */
.highlight .nb { color: #B58900 } /* Name.Builtin */
.highlight .nc { color: #268bd2 } /* Name.Class */
.highlight .no { color: #cb4b16 } /* Name.Constant */
.highlight .nd { color: #268bd2 } /* Name.Decorator */
.highlight .ni { color: #cb4b16 } /* Name.Entity */
.highlight .ne { color: #cb4b16 } /* Name.Exception */
.highlight .nf { color: #268bd2 } /* Name.Function */
.highlight .nl { color: #93a1a1 } /* Name.Label */
.highlight .nn { color: #93a1a1 } /* Name.Namespace */
.highlight .nx { color: #93a1a1 } /* Name.Other */
.highlight .py { color: #93a1a1 } /* Name.Property */
Source: Ethan Schoonover
The template filter needs to be loaded before it is used. Pygments will render the code inside of a <div>, so in most cases you can drop the filtered string right on the page.
objects/templates/objects/code_detail.html...
{% load custom_filters %}
{{ code.code|highlight_syntax:code.file_path|safe }}
...
Source: Kbruder Tech
To review, a string of code which is accessed from a model TextField is called and filtered in a template. The file_path field is also passed to the filter as an argument. If the file_field is not None, then Pygments will determine the lexer to use by the file name extension. If this fails, it will try to guess (it's pretty accurate). Once the lexer is chosen, the string is converted to colorful HTML.
Welcome! — Pygments
Pygments official site.
Custom template tags and filters | Django documentation | Django
You can extend the template engine by defining custom tags and filters using Python, and then make them available to your templates using the {% load %} tag.
🌐
https://ethanschoonover.com/
3MnhNRKgrpTFQWstYicjF6GebY7u7dap4u
Please donate some Bitcoin to our site. We can use it to keep developing Lookaway CMS.
MT61gm6pdp9rJoLyMWW5A1hnUpxAERnxqg
Please donate some Litecoin to our site. We can use it to keep developing Lookaway CMS.