0

我一直在努力学习 Wagtail,使用StreamFields 和 blocks 的复杂性让我头疼。遵循 Wagtail 文档和演示应用程序我没有遇到任何问题,但如果没有外部帮助,超越这些似乎是不可逾越的。

这是我正在尝试(并且失败)实现的目标。

在使用 Blog 应用程序时,我尝试对其进行扩展,以便StreamField允许添加基于pygments的带有语法突出显示的代码块。源代码由自定义StructBlock类(自然而然)管理,CodeBlock并且是StreamField. BlogPage在 Wagtail 管理员中,我可以输入代码、使用的语言、要应用的突出显示样式以及是否显示行号。这一切都工作得很好,直到我想根据所选的突出显示样式为渲染模板选择附加样式表。以下是页面模板如何包含样式表:

{% 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 %}

CodeBlockrender 方法中完成了它的工作(这个想法很感激 - 并且无耻地使用 - 在线)。在应用程序流程的这一点上,将突出显示样式添加到页面上下文为时已晚,因此我尝试在覆盖的 def get_context 函数中分解页面正文字段,但没有成功。我只是无法通过任何类成员访问底层 JSON 文本或访问数据BlogPage

有没有办法在从数据库加载数据之后和交给模板之前将突出显示样式添加到页面上下文?

这是我的基本博客页面:

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'),
]

这是我的BlogStreamBlock班级的定义:

class BlogStreamBlock(StreamBlock):
    subtitle = CharBlock(icon='title', classname='title')
    abstract = RichTextBlock(icon='pilcrow')
    paragraph = RichTextBlock()
    aligned_image = ImageBlock()
    source_code = CodeBlock()
    quote = QuoteBlock()

最后,这是CodeBlock课程:

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))
4

1 回答 1

0

几个小时的睡眠如何帮助您理清思维过程真是令人惊讶!很抱歉浪费了您的时间。

在管理编辑器中保存您的页面是不够的!您也必须发布它!

正如 gasman 在他的评论中所建议的那样,在 中覆盖get_context可以BlogPage让您直接访问body班级成员。在那里我可以遍历元素,检查它们block_type并访问它们的子元素,如下所示:

def get_context(self, request, *args, **kwargs):
    context = super(BlogPage, self).get_context(request)
    if self.body and len(self.body) > 0:
        for block in self.body:
            if block.block_type == 'source_code':
                context['has_code_block'] = True
                context['code_colorizer'] = block.value['colors'] if block.value['colors'] else 'default'
    return context

这将确保当页面上有源代码块时,CSS 样式表始终可用。

现在让我们解决上面代码中的另一个明显错误。用于设置样式表的模板代码不像发布的那样工作。它产生的是一些 URL 转义的文本,例如:

<link rel="stylesheet" type="text/css" href="/static/css/highlight_%7B%7B%20code_colorizer%20%7D%7D.css">

它实际上需要的是以下内容:

{% block extra_css %}
    {% if has_code_block %}
        {% if code_colorizer %}
            {% with 'css/highlight_'|add:code_colorizer|add:'.css' as colorizer_choice %}
                <link rel="stylesheet" type="text/css" href="{% static colorizer_choice %}">
            {% endwith %}
        {% else %}
            <link rel="stylesheet" type="text/css" href="{% static 'css/highlight_default.css' %}">
        {% endif %}
    {% endif %}
{% endblock %}

实际上,由于该get_context函数总是设置“默认”着色器,如果用户不选择一个,{% if code_colorizer %}检查及其{% else %}分支可以完全删除。

对于任何想要使用此代码的人,请注意,在同一页面上使用不同语法突出显示样式的多个代码块将无法按预期工作。实际上,页面类仅包括最后一个代码块的选定样式表。如果在每个代码块中选择了所有不同的 CSS 文件,我可以添加所有不同的 CSS 文件,但是由于 pygments 荧光笔使用相同的 CSS 类名,因此 HTML 文件中的多个样式仍然无法按您的意愿工作。当然,可以使用 pygments API为每个突出显示样式(在函数中)生成一个<div></div>带有特定类的封闭标记,然后编辑相应的 CSS 文件以将相同的类添加到每个元素,但这超出了这个问题的范围。renderCodeBlock

于 2017-01-26T16:00:39.413 回答