0

我想编写一个过滤器来替换我的流场文本中的一些 $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')
4

1 回答 1

2

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

概述:

  • 使用 Python 内置的基本模板系统(或Python 3 文档),这很容易,并且会为您省去直接处理替换的麻烦。
  • Python 的内置模板系统使用$variablename不上$variablename$,但效果很好,如果真的需要,可以配置。
  • 避免尝试手动构建流场中的块,最好只是str(self.streamfield)强制它呈现为漂亮的 HTML 之类的东西。
  • class Meta: template = ... 请记住,您可以使用查看文档为任何流块自定义 html 。
  • 一旦我们从流域中获得了 HTML 输出,我们就可以使用string.Template该类通过提供模板名称的字典以及替换它们的内容来创建输出文本。模板变量名称中不应包含符号$variablenamenot $variablename),库会为您处理,它还处理基本的字符串转换。
  • 为了简单起见,我使用了model_to_dictDjango 中的一个有用的 util 来将CityObjectdict 直接传递给模板(将其视为 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 %}
于 2018-01-24T11:09:10.723 回答