PinaxでDjango入門2 basic_projectのprofilesを変更 djangoの名前空間
January 18, 2009 at 11:01 PM | View Commentsハンズオンの課題その1
pinaxのbasic_projectのprofileをcomplete_projectのprofileに変更する
をやってみたいと思います。
以下動画、
http://www.ueblog.org/media/static/django6_1/d21.swf
http://www.ueblog.org/media/static/django6_1/d22.swf
http://www.ueblog.org/media/static/django6_1/d23.swf
今回はYouTubeにもアップしてみました。
動画を見てもらえば分かると思いますが、数々のエラーメッセージから判断して
- settings.py のROOT_URLCONFの確認
- settings.py のINSTALL_APPSに足りないアプリを設定する
- 名前空間から外れているアプリをコピーする
- テンプレートタグurl({% url hogehoge %})のnameの逆引きエラーを見つける
- templatesを置く場所の確認
などを行っています。
いろいろやってますが基本的にはどれも「Djangoプロジェクトの名前空間」の問題になります。
今回の例ではpinaxの外部から何か別のソースを用意したり、新しいコードを書いたわけでもないのですが、
complete_projectからは見えたappやtemplateがbasic_projectからは見えないという事例がいくつもありました。
この差がちゃんと理解できるようになればDjangoのプロジェクトで、どこになにを置いたらいいのか?または、置いてあるappやtemplateを使うためにはどうしたらいいのかが分かるようになると思います。
また、pinaxは通常の「django-admin.py startproject」で作ったプロジェクトでは見えない位置に置いたアプリやライブラリを読み込んでいます。
manage.pyを拡張しているのですが、その意味が分かれば
- レンタルサーバーなどで通常のsite-packagesなど置きたい場所にモジュールを置けない場合の対処
- 複数のProjectで共有しているDjangoのアプリの置き方
などが分かるようになると思います。
では、順番に見ていきましょう。
まず、前回動かしたpinax/projects/basic_projectをpinax/project/basic_project_2という名前でコピーして、syncdb&runserveをします。
% cd pinax/projects
% cp -r basic_project basic_project_2
% cd basic_project_2
% python manage.py syncdb
% python manage.py runserver 0.0.0.0:8001
しかし、ここではエラーが発生します。
No module named basic_project.url
これは、basic_project/urls.pyを探したけど見つからなかったというエラーです。なぜなら、いま動かしているのはbasic_project_2なので、basic_projectは現在のプロジェクトの名前空間にはないわけです。
動かすべきurls.pyは basic_project_2/urls.pyなので、それをDjangoに教えなくはいけません。具体的にはsettings.pyの
ROOT_URLCONF = 'basic_project_2.urls'
と設定してあげればOKです。
Djangoはsettings.pyの置いてあるプロジェクトは見えても、その隣にあるプロジェクトは見えないことがポイントです。
さて、このROOT_URLCONFは何かというと、ブラウザ等でアクセスがきたときのURLによって、それぞれ違う画面を表示するのがWEBアプリなのですが、その振り分けをするモジュールを設定しています。
実際にurls.pyがどのようになっているかというと
urlpatterns = patterns('',
url(r'^$', direct_to_template, {"template": "homepage.html"}, name="home"),
(r'^about/', include('about.urls')),
(r'^account/', include('account.urls')),
(r'^openid/(.*)', PinaxConsumer()),
(r'^profiles/', include('basic_profiles.urls')),
(r'^notices/', include('notification.urls')),
(r'^announcements/', include('announcements.urls')),
(r'^admin/(.*)', admin.site.root),
)
というようになっていて、
たとえば http://wwww.hoge.org/ がルートだとして
http://www.hoge.org/ だったらdirect_to_templateという関数に渡します。
渡される関数はviews関数と呼ばれ、リクエスト情報を元に実際の表示を担当する関数です。
includeというのは引数の別のurls.pyを展開して、そのあとの処理を任せるようになっています。
この辺りを全部説明すると長くなるので割愛しますが、ROOT_URLCONFに指定されたurls.pyはURLを振り分けて、実際に表示する関数(veiws関数)を呼び出すと理解しておいてください。
settings.pyとurls.pyのbasic_profilesを profilesに変更したあと、プロジェクトを動かすと
photos アプリをはじめ
- photos (projects/complete_project/appsの下)
- photolouge (apps/external_appsの下)
- app_plugins (apps/external_appsの下)
- friends (apps/external_appsの下
- microblogging (apps/local_appsの下)
- tribes (apps/local_appsの下)
- avatar (apps/external_appsの下)
- friends_app (projects/complete_project/appsの下)
と、様々なappが無いというエラーが発生します。
話すより書いた方がいいと思ったので動画ではしゃべってませんが
basic_project_2直下にないし、adminのようにdjango自体のアプリでないアプリがなぜ使うことができるか?ということが重要です。
pinaxで使われているmanage.pyはdjango-admin.py startprojectで生成されるmange.pyを拡張していて、
拡張している部分は
path = addsitedir(join(settings.PINAX_ROOT, "libs/external_libs"), set())
if path:
sys.path = list(path) + sys.path
sys.path.insert(0, join(settings.PINAX_ROOT, "apps/external_apps"))
sys.path.insert(0, join(settings.PINAX_ROOT, "apps/local_apps"))
sys.path.insert(0, join(settings.PROJECT_ROOT, "apps"))
のようになっています。
manage.pyを動かすときに、pinax/apps/external_apps, pinax/apps/local_appsと、そのプロジェクト直下のappsにPATHを通していて
(あと、pinax/libs/external_libs直下のライブラリにもPATHを通しています)
こうすることによって、複数のprojectで使いまわすappへのPATHを通すことを実現しているのです。
mange.pyを使えるのは開発時のみですが、各projectにはdeployというディレクトリがあって、mod_python, mod_wsgi, fastcgiで動かす場合のヒントがあるので本番時でも、それらを使うことで運用ができます。
これらはレンタルサーバーで活用するときにライブラリのインストールが通常のsite-packagesにできない場合など参考にするといいかもしれません。
ここではphotosとfriends_appはcomplete_project/appsの下にあり、名前空間から外れているのでbasic_projects_2/appsへコピーをしています。
さて、次は TemplateDoesNotExistのエラーです。
djangoはappの下にtemplatesというディレクトリがあれば、なにも設定しなくてもそこからtemplateファイルを探すのですがsettings.pyのTEMPLATE_DIRSに設定すればそちらを優先して探します。
pinaxのプロジェクトはプロジェクト直下のtemplatesを割り当てていたのですが、今回のprofilesとfriends_appに関してはcomplte_project/templatesの下にしかなく、どちらの下にもなかったので、
% cp -r complete_project/templates/profiles basic_projects_2/templates/profiles
% cp -r complete_project/templates/friends_app basic_projects_2/templates/friends_app
でコピーしました。
さて、最後のエラーはtemplate tagです。
Caught an exception while rendering: Reverse for 'basic_project_2.avatar_change' with arguments '()' and keyword arguments '{}' not found.
のようなエラーがでて、下の方に
<a href="{% url avatar_change %}"
の部分が赤くなっていたと思うのですが、
この {% url hogehoge %}はテンプレートタグと呼ばれるもので、中でもurlというのは、urls.pyの中で
(r'^about/', include('about.urls')),
のようなタプルではなく
url('^change/$', 'change', name='avatar_change'),
のようなurl関数を使って設定したもののなかから nameで指定されたURLを探し出すものです。
この場合は{% url avatar_change %}を http://localhost:8001/avatar/change/と置き換えます。
こうしておけば /change ではなく、別のURLに変更したいときに該当するurls.py一箇所を変えればテンプレートの変更をする必要がないので便利です。
しかも、今回のようにhttp://localhost::8001/avatar/changeをurls.pyに設定し忘れたときにエラーが発生するのも良いところです。
実際に変更するのはcomplete_project/urls.pyと同じように、basic_project_2/urls.pyに
(r'^tweet/', include('microblogging.urls')),
(r'^avatar/', include('avatar.urls')),
を追加すれば大丈夫です。
これで、profilesが一応complete_projectと同じような画面がでたと思います。
次はmicrobloggingの導入の予定です。
ただ、他のフレームワークや言語をやっている人がDjangoをちょっと触ってみたいという意図では
今回のハンズオンの課題は難しすぎたかもしれないので、もっと簡単なDjangoの基本を一回間に入れようかと思います。