Wagtail 教程 7:支持代码高亮

2019年1月9日


# Wagtail # Django # Python
  • Wagtail 是一个用 Python 编写的开源 CMS,基于 Django 框架构建。 它优雅、强大、敏捷,专注于灵活性和用户体验,为开发人员提供一个快速有吸引力的界面,可以直观地创建和组织内容。
  • Wagtail 教程系列 记录了基于 Wagtail 搭建博客站点的整个过程,当前站点 所呈现的即是搭建过程的最新效果。

使用WagtailCodeBlock实现代码高亮

https://github.com/FlipperPA/wagtailcodeblock

安装 Wagtail Code Block:

pip install wagtailcodeblock
或者
python3.6 -m pip install wagtailcodeblock

设置应用 wagtailcodeblock

修改 /slowread/settings/base.py ,加入应用 wagtailcodeblock:

INSTALLED_APPS = [
    ...
    'wagtailcodeblock',
    ...
]

设置代码高亮主题

WAGTAIL_CODE_BLOCK_THEME = None
或者
WAGTAIL_CODE_BLOCK_THEME = 'solarizedlight'

参考 PrismJS 提供的主题:

https://prismjs.com/index.html?theme=prism

可选主题名称列表如下:

None: Default
'coy': Coy
'dark': Dark
'funky': Funky
'okaidia': Okaidia
'solarizedlight': Solarized Light
'twilight': Twilight

设置代码语言

选择/设置常用代码语言:

WAGTAIL_CODE_BLOCK_LANGUAGES = (
    ('bash', 'Bash/Shell'),
    ('batch', 'Batch'),
    ('c', 'C'),
    ('csharp', 'C#'),
    ('cpp', 'C++'),
    ('css', 'CSS'),
    ('diff', 'diff'),
    ('django', 'Django/Jinja2'),
    ('docker', 'Docker'),
    ('git', 'Git'),
    ('html', 'HTML'),
    ('ini', 'Ini'),
    ('javascript', 'Javascript'),
    ('json', 'JSON'),
    ('markdown', 'Markdown'),
    ('perl', 'Perl'),
    ('php', 'PHP'),
    ('python', 'Python'),
    ('scss', 'SCSS'),
    ('sql', 'SQL'),
    ('plsql', 'PL/SQL'),
    ('swift', 'Swift'),
    ('typescript', 'TypeScript'),
    ('yaml', 'YAML'),
)

修改 /slowread/blog/models.py 中 class BlogPage(Page): 以下相应内容:

...

# body = RichTextField(blank=True)
body = StreamField([
    ('heading', blocks.CharBlock(classname="full title")),
    ('paragraph', blocks.RichTextBlock()),
    ('image', ImageChooserBlock()),
    ('code', CodeBlock(label='Code')),
    ('rawhtml', blocks.RawHTMLBlock(label='Raw HTML')),
])

...
# FieldPanel('body'),
StreamFieldPanel('body'),
...

修改后的完整版本如下:

class BlogPage(Page):
    author = models.CharField("Author", max_length=255, default="SlowRead.Net")
    date = models.DateField("Post date")
    intro = models.CharField(max_length=250)
    # body = RichTextField(blank=True)
    body = StreamField([
        ('heading', blocks.CharBlock(classname="full title")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('code', CodeBlock(label='Code')),
        ('rawhtml', blocks.RawHTMLBlock(label='Raw HTML')),
    ])
    tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
    categories = ParentalManyToManyField('blog.BlogCategory', blank=True)

    def main_image(self):
        gallery_item = self.gallery_images.first()
        if gallery_item:
            return gallery_item.image
        else:
            return None

    search_fields = Page.search_fields + [
        index.SearchField('intro'),
        index.SearchField('body'),
        index.SearchField('date'),
    ]

    content_panels = Page.content_panels + [
        MultiFieldPanel([
            FieldPanel('author'),
            FieldPanel('date'),
            FieldPanel('tags'),
            FieldPanel('categories', widget=forms.CheckboxSelectMultiple),
        ], heading="Blog information"),
        FieldPanel('intro'),
        # FieldPanel('body'),
        StreamFieldPanel('body'),
        InlinePanel('gallery_images', label="Gallery images"),
    ]

修改之前模板页面中对 page.body 的使用方式,之前是 richtext类型,现在是 StreamField类型:

之前:

{{ page.body|richtext }}

现在:

<article>
    {% for block in page.body %}
        {% if block.block_type == 'heading' %}
            <h1>{{ block.value }}</h1>
        {% else %}
            <section class="block-{{ block.block_type }}">
                {% include_block block %}
            </section>
        {% endif %}
    {% endfor %}
</article>

所有支持的语言:

WAGTAIL_CODE_BLOCK_LANGUAGES = (
    ('abap', 'ABAP'),
    ('actionscript', 'ActionScript'),
    ('ada', 'Ada'),
    ('apacheconf', 'Apache Configuration'),
    ('apl', 'APL'),
    ('applescript', 'AppleScript'),
    ('arduino', 'Arduino'),
    ('arff', 'ARFF'),
    ('asciidoc', 'AsciiDoc'),
    ('asm6502', '6502 Assembly'),
    ('aspnet', 'ASP.NET (C#)'),
    ('autohotkey', 'AutoHotkey'),
    ('autoit', 'AutoIt'),
    ('bash', 'Bash'),
    ('basic', 'BASIC'),
    ('batch', 'Batch'),
    ('bison', 'Bison'),
    ('brainfuck', 'Brainfuck'),
    ('bro', 'Bro'),
    ('c', 'C'),
    ('clike', 'C-like'),
    ('csharp', 'C#'),
    ('cpp', 'C++'),
    ('coffeescript', 'CoffeeScript'),
    ('clojure', 'Clojure'),
    ('crystal', 'Crystal'),
    ('csp', 'Content-Security-Policy'),
    ('css', 'CSS'),
    ('css-extras', 'CSS Extras'),
    ('d', 'D'),
    ('dart', 'Dart'),
    ('diff', 'Diff'),
    ('django', 'Django/Jinja2'),
    ('docker', 'Docker'),
    ('eiffel', 'Eiffel'),
    ('elixir', 'Elixir'),
    ('elm', 'Elm'),
    ('erb', 'ERB'),
    ('erlang', 'Erlang'),
    ('fsharp', 'F#'),
    ('flow', 'Flow'),
    ('fortran', 'Fortran'),
    ('gedcom', 'GEDCOM'),
    ('gherkin', 'Gherkin'),
    ('git', 'Git'),
    ('glsl', 'GLSL'),
    ('go', 'Go'),
    ('graphql', 'GraphQL'),
    ('groovy', 'Groovy'),
    ('haml', 'Haml'),
    ('handlebars', 'Handlebars'),
    ('haskell', 'Haskell'),
    ('haxe', 'Haxe'),
    ('http', 'HTTP'),
    ('hpkp', 'HTTP Public-Key-Pins'),
    ('hsts', 'HTTP Strict-Transport-Security'),
    ('ichigojam', 'IchigoJam'),
    ('icon', 'Icon'),
    ('inform7', 'Inform 7'),
    ('ini', 'Ini'),
    ('io', 'Io'),
    ('j', 'J'),
    ('java', 'Java'),
    ('javascript', 'JavaScript'),
    ('jolie', 'Jolie'),
    ('json', 'JSON'),
    ('julia', 'Julia'),
    ('keyman', 'Keyman'),
    ('kotlin', 'Kotlin'),
    ('latex', 'LaTeX'),
    ('less', 'Less'),
    ('liquid', 'Liquid'),
    ('lisp', 'Lisp'),
    ('livescript', 'LiveScript'),
    ('lolcode', 'LOLCODE'),
    ('lua', 'Lua'),
    ('makefile', 'Makefile'),
    ('markdown', 'Markdown'),
    ('markup', 'Markup'),
    ('markup-templating', 'Markup templating'),
    ('matlab', 'MATLAB'),
    ('mel', 'MEL'),
    ('mizar', 'Mizar'),
    ('monkey', 'Monkey'),
    ('n4js', 'N4JS'),
    ('nasm', 'NASM'),
    ('nginx', 'nginx'),
    ('nim', 'Nim'),
    ('nix', 'Nix'),
    ('nsis', 'NSIS'),
    ('objectivec', 'Objective-C'),
    ('ocaml', 'OCaml'),
    ('opencl', 'OpenCL'),
    ('oz', 'Oz'),
    ('parigp', 'PARI/GP'),
    ('parser', 'Parser'),
    ('pascal', 'Pascal'),
    ('perl', 'Perl'),
    ('php', 'PHP'),
    ('php-extras', 'PHP Extras'),
    ('plsql', 'PL/SQL'),
    ('powershell', 'PowerShell'),
    ('processing', 'Processing'),
    ('prolog', 'Prolog'),
    ('properties', '.properties'),
    ('protobuf', 'Protocol Buffers'),
    ('pug', 'Pug'),
    ('puppet', 'Puppet'),
    ('pure', 'Pure'),
    ('python', 'Python'),
    ('q', 'Q (kdb+ database)'),
    ('qore', 'Qore'),
    ('r', 'R'),
    ('jsx', 'React JSX'),
    ('tsx', 'React TSX'),
    ('renpy', 'Ren\'py'),
    ('reason', 'Reason'),
    ('rest', 'reST (reStructuredText)'),
    ('rip', 'Rip'),
    ('roboconf', 'Roboconf'),
    ('ruby', 'Ruby'),
    ('rust', 'Rust'),
    ('sas', 'SAS'),
    ('sass', 'Sass (Sass)'),
    ('scss', 'Sass (Scss)'),
    ('scala', 'Scala'),
    ('scheme', 'Scheme'),
    ('smalltalk', 'Smalltalk'),
    ('smarty', 'Smarty'),
    ('sql', 'SQL'),
    ('soy', 'Soy (Closure Template)'),
    ('stylus', 'Stylus'),
    ('swift', 'Swift'),
    ('tcl', 'Tcl'),
    ('textile', 'Textile'),
    ('tt2', 'Template Toolkit 2'),
    ('twig', 'Twig'),
    ('typescript', 'TypeScript'),
    ('vbnet', 'VB.Net'),
    ('velocity', 'Velocity'),
    ('verilog', 'Verilog'),
    ('vhdl', 'VHDL'),
    ('vim', 'vim'),
    ('visual-basic', 'Visual Basic'),
    ('wasm', 'WebAssembly'),
    ('wiki', 'Wiki markup'),
    ('xojo', 'Xojo (REALbasic)'),
    ('yaml', 'YAML'),
    ('xeora', 'Xeora'),
)

其他常用 StreamField

https://docs.wagtail.io/en/v2.4/topics/streamfield.html

修改 /slowread/blog/models.py 中 class BlogPage(Page): 以下相应内容:

body = StreamField([
    ('heading', blocks.CharBlock(classname="full title")),
    ('paragraph', blocks.RichTextBlock()),
    ('code', CodeBlock(label='Code')),
    ('image', ImageChooserBlock()),
    ('blockquote', CodeBlock(label='Block Quote')),
    ('documentchooser', CodeBlock(label='Document Chooser')),
    ('snippetchooser', CodeBlock(label='Snippet Chooser')),
    ('rawhtml', blocks.RawHTMLBlock(label='Raw HTML')),
])