【问题标题】:Django, custom template filters - regex problemsDjango,自定义模板过滤器 - 正则表达式问题
【发布时间】:2010-10-28 11:32:26
【问题描述】:

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

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]]替换为

  • &lt;a href="/Sites/wiki/ThisIsAWikiLink"&gt;This is the alt text&lt;/a&gt;

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

  • &lt;a href="/Sites/wiki/ThisIsAWikiLink/edit" class="redlink"&gt;This is the alt text&lt;/a&gt;

和返回值。

这是无效的代码(根据 cmets/answers 编辑):

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

代码需要做的是:

  • 提取 value 中的所有 WikiLink
  • 查询 Page 模型以查看页面是否存在
  • 用普通链接替换所有 WikiLink,其样式取决于每个 wiki 页面的存在。
  • 返回改变后的值

更新后的问题是: 什么正则表达式(方法)可以返回一个 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

【问题讨论】:

  • “它不工作”不是很有帮助。它在做什么?预期的输出是什么?
  • 您可能需要将^ $ 包裹在您的正则表达式周围。
  • -1:问题中缺少错误或错误结果。
  • 针对 cme​​ts 更新的问题
  • 我编辑了我的答案并添加了一个使用 re.sub 和回调的解决方案。它通过字符串实现你想要的。

标签: python regex django django-templates


【解决方案1】:

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

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

非常感谢所有答案!

【讨论】:

    【解决方案2】:

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

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

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

    编辑:

    关于如何修复代码的提示,我建议您使用re.sub with 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)
    

    【讨论】:

      【解决方案3】:

      代码:

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

      一般cmets:

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

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

      【讨论】:

      • 我知道注入攻击和性能问题。不过,它们稍后会修复(对我来说问题比正则表达式要小:))
      【解决方案4】:

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

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

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

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

      此外,乍一看,您的过滤器似乎是可利用的。您声称结果是安全的,但您没有过滤 alt 文本中的脚本标签等讨厌的东西。

      【讨论】:

      • 您对测试的看法是完全正确的。我的代码怎么没有那么远,例如。它不查询数据库 - 查看当前编辑。
      • 但它确实至少达到了我上面建议的第一个测试,它涵盖了在您访问数据库之前发生的代码。
      • 是的。我已经仔细检查了该模式是否有价值,并通过上面更简单的示例进行了证明。这是相同的正则表达式,但不查询数据库。我的失败在于假设 value 不会包含许多 WikiLink,例如。它一次会得到一个模式(不要问:))
      • 我在第一个 Wiki 实现中遇到了这个问题。当您预期某件事中的某一个时,测试 0、1 和 2 可以防止意外。
      猜你喜欢
      • 2019-06-27
      • 2020-11-13
      • 2021-08-16
      • 1970-01-01
      • 2012-09-24
      • 2014-11-06
      • 2011-05-28
      • 1970-01-01
      • 2011-10-04
      相关资源
      最近更新 更多