代码之家  ›  专栏  ›  技术社区  ›  hannson

Django,自定义模板过滤器-regex问题

  •  2
  • hannson  · 技术社区  · 16 年前

    我试图在Django中实现一个wikilink模板过滤器,它查询数据库模型,根据页面的存在给出不同的响应,与wikipedia的红色链接相同。过滤器不会引发错误,但不会对输入进行任何操作。

    维基林克 定义为: [[ThisIsAWikiLink | This is the alt text]]

    下面是一个不查询数据库的工作示例:

    from django import template
    from django.template.defaultfilters import stringfilter
    from sites.wiki.models import Page
    import re
    
    register = template.Library()
    
    @register.filter
    @stringfilter
    def wikilink(value):
        return re.sub(r'\[\[ ?(.*?) ?\| ?(.*?) ?\]\]', r'<a href="/Sites/wiki/\1">\2</a>', value)
    wikilink.is_safe = True
    

    这个 输入 ( value )是一个多行字符串,包含HTML和许多维基链接。

    预期的 输出 正在替代 [Thisisawikilink这是alt文本]] 具有

    • <a href="/Sites/wiki/ThisIsAWikiLink">This is the alt text</a>

      如果数据库中不存在“thisisawikilink”:

    • <a href="/Sites/wiki/ThisIsAWikiLink/edit" class="redlink">This is the alt text</a>

    并返回值。

    以下是非工作代码(根据注释/答案进行编辑):

    from django import template
    from django.template.defaultfilters import stringfilter
    from sites.wiki.models import Page
    import re
    
    register = template.Library()
    
    @register.filter
    @stringfilter
    def wikilink(value):
        m = re.match(r'\[\[ ?(.*?) ?\| ?(.*?) ?\]\]', value)
    
        if(m):
            page_alias = m.group(2)
            page_title = m.group(3)
            try:
                page = Page.objects.get(alias=page_alias)
                return re.sub(r'(\[\[)(.*)\|(.*)(\]\])', r'<a href="Sites\/wiki\/\2">\3</a>', value)
            except Page.DoesNotExist:
                 return re.sub(r'(\[\[)(.*)\|(.*)(\]\])', r'<a href="Sites\/wiki\/\2\/edit" class="redlink">\3</a>', value)
        else:
            return value
    wikilink.is_safe = True
    

    代码需要做的是:

    • 提取中的所有维基链接 价值
    • 查询 查看页面是否存在的模型
    • 用普通链接替换所有的wikilinks,其样式取决于每个wikipage的存在。
    • 归还修改过的 价值

    更新后的问题是: 什么正则表达式(方法)可以返回一个wikilinks的python列表,这个列表可以被修改,并用于替换原始匹配项(修改后)。

    编辑:

    我想这样做:

    def wikilink(value):
        regex = re.magic_method(r'\[\[ ?(.*?) ?\| ?(.*?) ?\]\]', value)
    
        foreach wikilink in regex:
             alias = wikilink.group(0)
             text = wikilink.group(1)
    
             if(alias exists in Page):
                  regex.sub("<a href="+alias+">"+ text +"</a>")
             else:
                  regex.sub("<a href="+alias+" class='redlink'>"+ text +"</a>")
    
        return value
    
    4 回复  |  直到 16 年前
        1
  •  4
  •   Ayman Hourieh    16 年前

    如果字符串中除wiki链接外还包含其他文本,则由于您正在使用 re.match 而不是 re.search . 重赛 在字符串的开头匹配。 重新搜索 匹配字符串中的任何位置。见 matching vs. searching .

    另外,你的正则表达式使用贪婪 * ,因此,如果一行包含多个wiki链接,它将不起作用。使用 *? 相反,让它不贪婪:

    re.search(r'\[\[(.*?)\|(.*?)\]\]', value)
    

    编辑:

    关于如何修复代码的提示,我建议您使用 re.sub with a callback . 其优点是:

    • 如果您在同一行中有多个wiki链接,它将正常工作。
    • 一次越过绳子就足够了。你不需要通行证就可以找到维基链接,也不需要另一个通行证就可以进行替换。

    以下是实施的示意图:

    import re
    
    WIKILINK_RE = re.compile(r'\[\[(.*?)\|(.*?)\]\]')
    
    def wikilink(value):
      def wikilink_sub_callback(match_obj):
        alias = match_obj.group(1).strip()
        text = match_obj.group(2).strip()
        if(alias exists in Page):
          class_attr = ''
        else:
          class_attr = ' class="redlink"'
        return '<a href="%s"%s>%s</a>' % (alias, class_attr, text)
    
      return WIKILINK_RE.sub(wikilink_sub_callback, value)
    
        2
  •  3
  •   Dave W. Smith    16 年前

    这类问题很快就落在一组小的单元测试中。

    可以单独测试的过滤器片段(通过一点代码重组):

    • 确定值是否包含要查找的模式
    • 如果有匹配的页,会生成什么字符串
    • 生成的字符串是没有匹配的页

    这将帮助你隔离出哪里出了问题。您可能会发现需要重新连接regexps以考虑周围的可选空格。

    而且,乍一看,您的过滤器似乎是可利用的。您声称结果是安全的,但是您没有为脚本标记之类的错误过滤alt文本。

        3
  •  1
  •   Matthew Christensen    16 年前

    代码:

    import re
    
    def page_exists(alias):
        if alias == 'ThisIsAWikiLink':
            return True
    
        return False
    
    def wikilink(value):
        if value == None:
            return None
    
        for alias, text in re.findall('\[\[\s*(.*?)\s*\|\s*(.*?)\s*\]\]',value):
            if page_exists(alias):
                value = re.sub('\[\[\s*%s\s*\|\s*%s\s*\]\]' % (alias,text), '<a href="/Sites/wiki/%s">%s</a>' % (alias, text),value)            
            else:
                value = re.sub('\[\[\s*%s\s*\|\s*%s\s*\]\]' % (alias,text), '<a href="/Sites/wiki/%s/edit/" class="redtext">%s</a>' % (alias, text), value)
    
        return value
    

    样品结果:

    >>> import wikilink
    >>> wikilink.wikilink(None)
    >>> wikilink.wikilink('')
    ''
    >>> wikilink.wikilink('Test')
    'Test'
    >>> wikilink.wikilink('[[ThisIsAWikiLink | This is the alt text]]')
    '<a href="/Sites/wiki/ThisIsAWikiLink">This is the alt text</a>'
    >>> wikilink.wikilink('[[ThisIsABadWikiLink | This is the alt text]]')
    '<a href="/Sites/wiki/ThisIsABadWikiLink/edit/" class="redtext">This is the alt text</a>'
    >>> wikilink.wikilink('[[ThisIsAWikiLink | This is the alt text]]\n[[ThisIsAWikiLink | This is another instance]]')
    '<a href="/Sites/wiki/ThisIsAWikiLink">This is the alt text</a>\n<a href="/Sites/wiki/ThisIsAWikiLink">This is another instance</a>'
    >>> wikilink.wikilink('[[ThisIsAWikiLink | This is the alt text]]\n[[ThisIsAWikiLink | This is another instance]]')
    

    一般意见:

    • 芬德尔 你要找的是魔法的功能吗?
    • 变化 存在的页面 运行所需的任何查询
    • 易受HTML注入攻击(如上述Dave W.Smith所述)
    • 在每次迭代中必须重新编译regex效率很低
    • 每次查询数据库效率低下

    我认为使用这种方法会很快遇到性能问题。

        4
  •  0
  •   hannson    16 年前

    这是工作代码,以防有人需要它:

    from django import template
    from django.template.defaultfilters import stringfilter
    from sites.wiki.models import Page
    import re
    
    register = template.Library()
    
    @register.filter
    @stringfilter
    def wikilink(value):
      WIKILINK_RE = re.compile(r'\[\[ ?(.*?) ?\| ?(.*?) ?\]\]')
    
      def wikilink_sub_callback(match_obj):
        alias = match_obj.group(1).strip()
        text = match_obj.group(2).strip()
    
        class_attr = ''
        try:
            Page.objects.get(alias=alias)
        except Page.DoesNotExist:
            class_attr = ' class="redlink"'
        return '<a href="%s"%s>%s</a>' % (alias, class_attr, text)
    
      return WIKILINK_RE.sub(wikilink_sub_callback, value)
    wikilink.is_safe = True
    

    非常感谢您的回答!