djangoをmod_wsgiやcgiで使うためのpythonスクリプト
mod_wsgiを使うことでも、djangoアプリケーションが使えます:
とはいえ、このドキュメントも、.htaccessで使うためのものはなく、いろいろな環境の対策が混在してるために自環境に合わせる部分の識別が難しいものになっています。
しかも、django(1.0.2)のWSGIHandler自体にも、runserver等と振る舞いが一致しない、という問題があります。
以下は、この問題とその解決法を示し、環境を含めmod_wsgi上で動かすための構成を示し、さらにCGIとして使うため方法も示します。
Problems about: django.core.handlers.wsgi.WSGIHandler
django-1.0.2のWSGIHandlerは、wsgirefのPATH_INFOを元にurlの解決を行います。しかしこのせいで、URLの解釈方法に、runserverやmod_pythonでの通常のhandlerとの違いが出てしまいます。
具体的には、以下のREQUEST_URIになるようなURLでアクセスしたとき、各実装では以下のようなデータで、viewsの関数の引数に渡ることになります:
- REQUEST_URI
- '/foo/%7ebar//buzz'
- runserver, modpython
- '/foo/~bar//buzz'
- mod_wsgi, cgi(via wsgiref)
- '/foo/~bar/buzz'
以下のスクリプトは、runserverやmodpythonとの差をなくすワークアラウンドを入れた、cgiやmod_wsgiでWSGIHandler経由でdjangoを使うための起動スクリプトです。
index.wsgi/index.cgi
#!/usr/bin/env python # -*- coding: utf-8 mode: python -*- import sys import os # modify if you put "index.wsgi" into different directory # apps = os.path.dirname(os.path.join("where", "to", "myproject", "settings.py")) apps = os.path.dirname(__file__) project = os.path.dirname(apps) sys.path.append(apps) sys.path.append(project) # modify if you use custom setting # os.environ["DJANGO_SETTINGS_MODULE"] = "myproject.mysql_settings" os.environ["DJANGO_SETTINGS_MODULE"] = "myproject.settings" import django.core.handlers.wsgi # application = django.core.handlers.wsgi.WSGIHandler() # # django.core.handlers.wsgi.WSGIRequest uses PATH_INFO for urlresolvers # but it has diffrent semantics against runserver/mod_python . # # for example: REQUEST_URI: '/foo/%7ebar//buzz' # runserver/modpython : '/foo/~bar//buzz' # wsgi : '/foo/~bar/buzz' # # WORKAROUND: # set PATH_INFO = REQUEST_URI - SCRIPT_NAME - ("?" + QUERY_STRING) def application(environ, start_response): import urllib path = urllib.splitquery(environ["REQUEST_URI"])[0] path = urllib.unquote(path) path_info = path[len(environ["SCRIPT_NAME"]):] environ["PATH_INFO"] = path_info return django.core.handlers.wsgi.WSGIHandler()(environ, start_response) if __name__ == "__main__": from wsgiref.handlers import CGIHandler CGIHandler().run(application) pass
wsgiref.handlers.CGIHandlerを使用することで、cgiでもmod_wsgiでも同じコードで動くようにしています。
Usage: mod_wsgi
ディレクトリ構成
/where/to/myproject/settings.py manage.py ... ~/public_html/myproject/.htaccess ~/public_html/myproject/index.wsgi
.htaccess
# .htaccess AddHandler wsgi-script .wsgi Options +ExecCGI DirectoryIndex index.wsgi
Usage: cgi
ディレクトリ構成
/where/to/myproject/settings.py manage.py ... ~/public_html/myproject/.htaccess ~/public_html/myproject/index.cgi
.htaccess
# .htaccess AddHandler cgi-script .cgi Options +ExecCGI DirectoryIndex index.cgi
Appendix: env.wsgi/env.cgi
#!/usr/bin/env python # -*- coding: utf-8 mode: python -*- import os def to_table(dic): keys = [key for key in dic] keys.sort() code = "<table style='border:solid'>" for key in keys: code += "<tr><th>%s</th><td>%s</td></tr>" % (key, dic[key]) pass code += "</table>" return code def application(environ, start_response): status = "200 OK" headers = [('Content-Type', "text/html;charset=UTF-8")] code = "<html><body>" code += "<h3>environ</h3>" code += to_table(environ) code += "<h3>os.environ</h3>" code += to_table(os.environ) code += "</body></html>" start_response(status, headers) return [code] if __name__ == "__main__": from wsgiref.handlers import CGIHandler CGIHandler().run(application) pass