I have been trying to learn Wagtail and the intricacies of using StreamField
s and blocks are giving me a headache. I had no problems following the Wagtail docs and the demo app, but going beyond that seems to be insurmountable without external help.
Here is what I am trying (and failing) to achieve.
When playing around with the Blog application, I tried to extend it so the StreamField
allows for adding of code blocks with syntax highlighting, based on pygments. The source code is managed by a custom StructBlock
class (CodeBlock
, naturally) and is part of the StreamField
body of the BlogPage
. In Wagtail admin I can enter code, used language, what highlight style to apply and whether to show line numbers or not. This all works perfectly fine, up to the point where I would like to select the additional stylesheet for the rendering template based on the selected highlight style. Here is how the page template would include the stylesheet:
{% block extra_css %}
{# This goes in the page <head> section #}
{% if has_code_block %}
{% if code_colorizer %}
<link rel="stylesheet" type="text/css" href="{% static 'css/highlight_{{ code_colorizer }}.css' %}">
{% else %}
<link rel="stylesheet" type="text/css" href="{% static 'css/highlight_default.css' %}">
{% endif %}
{% endif %}
{% endblock %}
The CodeBlock
does its job in the render method (idea gratefully found - and shamelessly used - online). At this point in the app flow it's too late to add the highlight style to the page context, so I tried to decompose the page body field in an overridden def get_context function, without success. I just haven't been able to get to the underlying JSON text or access the data through any of the class members from withing the BlogPage
class.
Is there a way to add the highlight style to the page context just after the data is loaded from the DB and before given over to the template?
Here is my basic blog page:
class BlogPage(Page):
tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
posted_date = models.DateField("Post date")
edited_date = models.DateField("Edited date", null=True, blank=True)
feed_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+')
body = StreamField(BlogStreamBlock)
search_fields = Page.search_fields + [
index.SearchField('body')
]
subpage_types = []
parent_page_types = ['BlogIndexPage']
@property
def blog_index(self):
return self.get_ancestors().type(BlogIndexPage).last()
BlogPage.content_panels = [
FieldPanel('title', classname='full title'),
FieldPanel('posted_date'),
FieldPanel('edited_date'),
StreamFieldPanel('body'),
InlinePanel('related_links', label="Related links"),
]
BlogPage.promote_panels = Page.promote_panels + [
ImageChooserPanel('feed_image'),
FieldPanel('tags'),
]
This is the definition of my BlogStreamBlock
class:
class BlogStreamBlock(StreamBlock):
subtitle = CharBlock(icon='title', classname='title')
abstract = RichTextBlock(icon='pilcrow')
paragraph = RichTextBlock()
aligned_image = ImageBlock()
source_code = CodeBlock()
quote = QuoteBlock()
And finally, here is the CodeBlock
class:
class CodeBlock(StructBlock):
LANGUAGE_CHOICES = (
('aspx-cs', '.NET ASP/C#'),
('aspx-vb', '.NET ASP/VisualBasic'),
('csharp', '.NET C#'),
('fsharp', '.NET F#'),
('vbnet', '.NET VisualBasic'),
('ng2', 'Angular 2'),
('html+ng2', 'Angular 2 Html'),
('apache', 'Apache Config'),
('arduino', 'Arduino Sketch'),
('asm', 'Assembly'),
('bash', 'Bash Shell'),
('batch', 'Batch CMD File'),
('c', 'C'),
('cpp', 'C++'),
('cmake', 'CMake'),
('coffeescript', 'Coffee Script'),
('css', 'CSS'),
# ... and many, many more ...
('vhdl', 'Vhdl'),
('registry', 'Windows Registry'),
('xml', 'XML'),
('xml+php', 'XML/PHP'),
('xslt', 'XSLT'),
('yaml', 'Yaml'),
)
COLORIZER_CHOICES = (
('abap', 'Abap'),
('algol', 'Algol'),
('algol_nu', 'Algol Nu'),
# ... finish the list with all the highlight styles in the current version of pygments
('vs', 'Vs'),
('xcode', 'Xcode'),
)
language = ChoiceBlock(choices=LANGUAGE_CHOICES, classname='full')
colors = ChoiceBlock(choices=COLORIZER_CHOICES, classname='full')
code = TextBlock()
line_numbers = BooleanBlock(classname='full')
class Meta:
icon = 'code'
def render(self, value, context=None):
src = value['code'].strip('\n');
lang = value['language']
line_nos = value['line_numbers']
lexer = get_lexer_by_name(lang)
formatter = get_formatter_by_name('html', linenos='table' if line_nos else False, cssclass='codehilite', style='default',noclasses=False)
return mark_safe(highlight(src, lexer, formatter))
get_context
method? Accessingself.body
should definitely give you the data you're interested in. – gasman