2

我正在尝试在 Django 中实现一个 WikiLink 模板过滤器,它查询数据库模型以根据页面的存在给出不同的响应,与维基百科的红色链接相同。过滤器不会引发错误,而是不对输入做任何事情。

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

输入( ) 是一个多行字符串,包含 HTML 和许多 WikiLink value

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

  • <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
  • 查询Page模型以查看页面是否存在
  • 用普通链接替换所有 WikiLink,其样式取决于每个 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

4 回答 4

4

如果您的字符串包含除 wiki 链接之外的其他文本,则您的过滤器将不起作用,因为您使用re.match的是re.search. re.match匹配字符串的开头。re.search匹配字符串中的任何位置。请参阅匹配与搜索

此外,您的正则表达式使用 greedy *,因此如果一行包含多个 wiki 链接,它将不起作用。改为*?使其不贪心:

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

编辑:

至于如何修复代码的提示,我建议您使用re.subwith a callback。优点是:

  • 如果您在同一行中有多个 wiki 链接,它可以正常工作。
  • 一次通过字符串就足够了。您不需要通行证来查找 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)
于 2009-05-23T19:28:33.963 回答
3

这种类型的问题很快就会落到一小组单元测试中。

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

  • 确定 value 是否包含您正在寻找的模式
  • 如果有匹配的页面会生成什么字符串
  • 生成的字符串是没有匹配的页面

这将帮助您找出问题所在。您可能会发现您需要重新连接正则表达式以考虑 | 周围的可选空格。

此外,乍一看,您的过滤器似乎是可利用的。您声称结果是安全的,但您没有过滤 alt 文本以查找诸如脚本标签之类的讨厌内容。

于 2009-05-23T19:24:19.913 回答
1

代码:

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]]')

普通的留言:

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

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

于 2009-05-23T21:57:04.490 回答
0

如果有人需要,这是工作代码:

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

非常感谢所有的答案!

于 2009-05-24T01:38:17.600 回答