Wagtail:如何在model.py中的流域中过滤文本

时间:2018-01-24 08:25:24

标签: python django wagtail wagtail-streamfield

我想编写一个过滤器来替换流域文本中的一些$ variables $。在我的“页面”模型中执行此操作的最佳方法是什么?我尝试了以下操作,但如果我将模型保存为草稿并在之后发布,则有时无效。有没有人知道更好的方法呢?

class CityPage(Page, CityVariables):

    cityobject = models.ForeignKey(CityTranslated, on_delete=models.SET_NULL, null=True, blank=True)
    streamfield  = StreamField(BasicStreamBlock, null=True, blank=True)

    content_panels = Page.content_panels + [
        FieldPanel('cityobject', classname="full"),
        StreamFieldPanel('streamfield'),

    ]

    def get_streamfield(self):
        for block in self.streamfield:
            if type(block.value) == unicode:
                block.value = self.replace_veriables(block.value)
            elif type(block.value) == RichText:
                block.value.source = self.replace_veriables(block.value.source)
            else:
                print "notimplemented"
        return self.streamfield

这只是用我的数据库中的值替换$ variables $的类。

class CityVariables():

    def replace_veriables(self, repstr):
        reprules = self.get_city_context()
        for key, value in reprules.iteritems():
            repstr = repstr.replace(key, value)
        return repstr

    def get_city_context(self):
        context = {}
        if self.cityobject.population:
            context['$population$'] = unicode(self.cityobject.population)
        if self.cityobject.transregion:
            context['$region$'] = unicode(self.cityobject.transregion)
        return context


class BasicStreamBlock(blocks.StreamBlock):
    h2              = blocks.CharBlock(icon="title", classname="title")
    h3              = blocks.CharBlock(icon="title", classname="title")
    h4              = blocks.CharBlock(icon="title", classname="title")
    h5              = blocks.CharBlock(icon="title", classname="title")
    paragraph       = blocks.RichTextBlock(icon="pilcrow")
    image           = ImageChooserBlock(label="Image", icon="image")
    aligned_html    = blocks.RawHTMLBlock(icon="code", label='Raw HTML')

1 个答案:

答案 0 :(得分:1)

这是一种从CityPage模型中简单地从streamfield生成模板化(转换)html输出的方法。

<强>概述:

  • 使用内置basic Template system(或Python 3 docs)的Python,这很简单,可以省去直接处理替换的麻烦。
  • Python内置的模板系统使用$variablename而非$variablename$,但效果很好,可以在真正需要时进行配置。
  • 避免尝试手动在流场中构建块,最好是str(self.streamfield)这样会强制它渲染成漂亮的HTML。
  • 请记住,您可以使用class Meta: template = ... see docs自定义任何streamblock的html。
  • 一旦我们从流域获得HTML输出,我们就可以使用string.Template类来创建输出文本,方法是提供模板名称的字典以及替换它们的内容。模板变量名称其中包含$符号(variablename而非$variablename),库会为您处理,它也会照顾基本字符串转换。
  • 为了简单起见,我使用了Django的一个有用的model_to_dict util来使CityObject成为一个直接传递给模板的dict(想想这是Django模板的上下文)。
  • 注意:这意味着您的$region无效,需要匹配字段名称,例如。 $transregion - 或者只是更改字段名称。如果所有变量/字段名都匹配,它可以让以后更容易阅读代码。
  • 在我们最终city_page.html模板中使用此输出之前,我们需要将其标记为Django直接呈现的安全。 重要提示:请注意这一点,因为这意味着有人可以将javascript代码保存到CityObject中并在前端运行,您可能需要model_to_dict之后的其他图层来清除任何潜在的js代码。

示例:myapp / models.py

from django.forms.models import model_to_dict
from django.utils.safestring import mark_safe

from string import Template
# other imports... Page, etc


class CityPage(Page):

    cityobject = models.ForeignKey(
        CityTranslated, on_delete=models.SET_NULL, null=True, blank=True)
    streamfield = StreamField(BasicStreamBlock, null=True, blank=True)

    content_panels = Page.content_panels + [
        FieldPanel('cityobject', classname="full"),
        StreamFieldPanel('streamfield'),
    ]

    def get_templated_streamfield(self):
        # using str is a quick way to force the content to be rendered
        rendered_streamfield = str(self.streamfield)
        # will generate a dict eg. {'population': 23000, 'transregion': 'EU'}
        # will not convert to values string/unicode - but this is handled by Template
        template_variables = model_to_dict(self.cityobject)
        template = Template(rendered_streamfield)
        # using safe_substitute will **not** throw an error if a variable exists without a value
        converted = template.safe_substitute(template_variables)
        # as we have html markup we must mark it as safe
        return mark_safe(converted)

示例:myapp / template / city_page.html

{% extends "base.html" %}
{% load wagtailimages_tags %}

{% block content %}
    {% include "base/include/header.html" %}
    <div class="container">
        <div class="row">
            <div class="col-md-6">
                <em>Streamfield Original (without templating)</em>
                {{ page.streamfield }}
            </div>
            <div class="col-md-2">
                <em>Streamfield with templating</em>
                {{ page.get_templated_streamfield }}
            </div>
        </div>
    </div>
{% endblock content %}