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

2024-06-28 19:46:13 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图在Django中实现一个WikiLink模板过滤器,它根据页面的存在情况查询数据库模型以给出不同的响应,这与Wikipedia的red links相同。过滤器不会引发错误,但不会对输入执行任何操作。在

WikiLink定义为:[[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和许多wikilink。在

预期的输出[[ThisIsAWikiLink | This is the alt text]]替换为

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

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

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

以及返回值。在

以下是非工作代码(根据评论/答案编辑):

^{pr2}$

代码需要做的是:

  • 提取value中的所有WikiLinks
  • 查询页面模型以查看该页面是否存在
  • 用普通链接替换所有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

Tags: thetextfromimportisvaluewikialias
3条回答

如果您的字符串中除了wiki链接之外还包含其他文本,那么您的过滤器将无法工作,因为您使用的是re.match,而不是{}。re.match匹配字符串的开头。re.search匹配字符串中的任何位置。见matching vs. searching。在

另外,您的regex使用贪婪的*,因此如果一行包含多个wiki链接,那么它将无法工作。使用*?使其非贪婪:

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

编辑:

至于如何修复代码的提示,我建议您使用^{} with a callback。优点是:

  • 如果在同一行中有多个wiki链接,则它可以正常工作。在
  • 一次穿过绳子就够了。你不需要一个通行证来查找wiki链接,而需要另一个通行证来进行替换。在

以下是实施的示意图:

^{pr2}$

这类问题很快就会落到一小部分单元测试中。在

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

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

这会帮助你隔离出哪里出了问题。您可能会发现,需要重新布线regexp,以考虑|周围的可选空格。在

而且,乍一看,你的过滤器似乎是可以利用的。您声称结果是安全的,但是您没有过滤alt文本中的脚本标记之类的恶心。在

代码:

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

样本结果:

^{pr2}$

一般评论:

  • findall是你要找的神奇功能
  • 更改page_exists以运行您想要的任何查询
  • 易受HTML注入攻击(如上文Dave W.Smith所述)
  • 每次迭代都必须重新编译正则表达式是低效的
  • 每次查询数据库效率低下

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

相关问题 更多 >