[TurboGears] redirect関数の引数にurl関数の戻り値を与えてはならない。
環境
この記事の内容は、TurboGears 1.0.1で確認しました。
redirect
TurboGearsではリダイレクトするのにturbogears.controllers.redirect関数を使用します。
redirect("/foo/")
一方、URLの先頭に設定ファイルのserver.webpathの値を加えるturbogears.controllers.url関数があります。
path = url("/foo/")
server.webpathが"/bar"である場合、上のpath変数の値は"/bar/foo/"になります。
ここで、このurl関数の戻り値をredirect関数に与えてはいけません。すなわち、
redirect(url("/foo/"))
とするのは誤りです。このようにした場合、server.webpathの設定が"/bar"であった場合、リダイレクト先が"/bar/bar/foo/"になります。
というのも、redirect関数内でurl関数を呼び出しているからです。recirect関数は、以下のように実装されています。
def redirect(redirect_path, redirect_params=None, **kw): """ Redirect (via cherrypy.HTTPRedirect). Raises the exception instead of returning it, this to allow users to both call it as a function or to raise it as an exeption. """ raise cherrypy.HTTPRedirect( url(tgpath=redirect_path, tgparams=redirect_params, **kw))
したがって、web.serverpathを設定している場合でも、リダイレクトするときは、
redirect("/foo/")
と、コンテキスト内のパスだけ指定すればよいです。
所感
すぐに原因は分かりましたが、これに引っかかりました。ビューの内部では、
tg.url("/foo/")
としてweb.serverpathを設定する必要があるのだから、リダイレクトも同じだろうとずっと思っていました。判明したのは実働環境に移してからで、そのときには既に多くの箇所でこの間違いをしていました。結局、
# ruby require "fileutils" require "find" Find.find(".") do |path| next if /\.py\Z/ !~ path orig = "#{path}.orig" if !FileTest.file?(orig) FileUtils.cp(path, orig, :preserve => true) end tmp = "#{path}.tmp" open(tmp, "w") do |dest| open(path) do |src| src.each do |line| while m = line.match(/\A(.*)redirect\(url\("(.*)"\)\)(.*)\Z/) line = m[1] + "redirect(\"#{m[2]}\")" + m[3] end dest.write(line) end end end FileUtils.mv(tmp, path) end # vim: tabstop=2 shiftwidth=2 expandtab
というRubyスクリプトを書いてまとめて変換しました。