2

ウィキペディアの赤いリンクと同じように、ページの存在に応じて異なる応答を返すようにデータベース モデルをクエリする WikiLink テンプレート フィルターを Django に実装しようとしています。フィルターはエラーを発生させませんが、代わりに入力に対して何もしません。

ウィキリンクは次のように定義されています。[[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

入力( ) は、valueHTML と多くの WikiLinks を含む複数行の文字列です。

期待される出力[[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

コードで行う必要があるのは次のとおりです。

  • 内のすべてのウィキリンクを抽出する
  • Pageモデルにクエリを実行して、ページが存在するかどうかを確認します
  • すべてのウィキリンクを通常のリンクに置き換え、各ウィキページの存在に応じてスタイルを設定します。
  • 変更された値を返す

更新された質問は次のとおりです。どの正規表現 (メソッド) が python WikiLinks のリストを返すことができますか?これは変更され、元の一致を置き換えるために使用できます (変更された後)。

編集:

私はこのようなことをしたいと思います:

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.searchre.match文字列の先頭に一致します。re.search文字列内の任意の場所に一致します。マッチングと検索を参照してください。

また、あなたの正規表現は greedy*を使用しているため、1 行に複数の wiki リンクが含まれていると機能しません。代わりに使用*?して、貪欲でないようにします。

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

編集:

コードを修正する方法のヒントとしてre.sub、 callbackを使用することをお勧めします。利点は次のとおりです。

  • 同じ行に複数の wiki リンクがある場合、正しく機能します。
  • ストリングを 1 回通すだけで十分です。ウィキ リンクを検索するためのパスは必要ありません。また、置換を行うための別のパスも必要ありません。

実装のスケッチは次のとおりです。

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

これは、単体テストの小さなセットにすぐに分類されるタイプの問題です。

分離してテストできるフィルターの一部 (少しコードを再構築します):

  • 値に探しているパターンが含まれているかどうかを判断する
  • 一致するページがある場合に生成される文字列
  • 生成される文字列は、一致するページがない場合です

これは、問題が発生している場所を特定するのに役立ちます。| の周りのオプションのスペースを考慮して、正規表現を再配線する必要があることに気付くでしょう。

また、一見すると、フィルタが悪用可能であるように見えます。結果は安全だと主張していますが、スクリプト タグなどの厄介な代替テキストをフィルタリングしていません。

于 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は、探している魔法の re 関数です
  • 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 に答える