Django:GAEでdjango_openid_authを使ってOpenID認証を実装する その2

Django:GAEでdjango_openid_authを使ってOpenID認証を実装する その1の続きです。django-nonrelを使ってます。

ログアウトする方法と、画面にログイン、ログアウトの表示を出す方法がわかったので書いときます。

まず、ログアウトはdjangoの認証のログアウトを呼び出せば普通にログアウトできました(汗。勉強が足りなかったです…。その方法も引き続き書きますが。

まず、ログアウトするためのURLをurls.pyに登録します。

urlpatterns = patterns('',
    url(r'^$', 'sample.views.index'),
    url(r'^logout/', 'sample.views.logout_view', name='logout'),
    url(r'^_ah/warmup$', 'djangoappengine.views.warmup'),
    # openid
    (r'^openid/', include('django_openid_auth.urls')),
)

次に、sample applicationのviews.pyに、logout_viewを定義します。
リダイレクト先はhttp://localhost:8000/にしてます。
また、indexのrender_to_responseで、RequestContextをtemplateに渡す処理をしています。これで、テンプレート側でrequestインスタンスが使えます。

from django.contrib.auth import logout
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template.context import RequestContext
from sample.sampleform import SampleForm

def index(request):
    form = SampleForm()
    return render_to_response('sample/index.html',
                              {'form': form},
                              context_instance=RequestContext(request)
                              )

def logout_view(request):
    logout(request)
    return HttpResponseRedirect('/')

requestをテンプレート側でも使うためには、settings.pyに以下の設定が必要らしい?。まだよくわかってませんが、認証情報をrequestに持たせるのには必要っぽいと。解釈が間違ってたらすみません…。

TEMPLATE_CONTEXT_PROCESSORS = (
    'django.contrib.auth.context_processors.auth',
    'django.core.context_processors.request',
    'django.core.context_processors.media',
)

最後に、テンプレート自体にログイン、ログアウトの書き分けの処理を追加します。私の場合は、どのページでもヘッダにログイン状況を表示させたかったので、templates/base.htmlに追加しました(base.htmlは全てのテンプレートして使っているhtml)。

<div id="header-user">
	{% if request.user.is_authenticated %}
		<a href="{% url sample.views.logout_view %}">ログアウト</a>
	{% else %}
		<a href="{% url django_openid_auth.views.login_begin %}">ログイン</a>
	{% endif %}
</div>

これで、ログインしている場合はログアウトのリンクが、ログインしてない場合はログインへのリンクが表示されました。django_openid_authのOpenID認証画面はかなり寂しいうえに使いにくいので、カスタマイズが必要ですが、とりあえずこれでOpenID認証ができるようになりました。よかったよかった!!


Django:GAEでdjango_openid_authを使ってOpenID認証を実装する その1

※書いている途中で見つけてしまったので貼っておきます。
GitHub: django-nonrel-gae-openid-auth
たぶんこれをチェックアウトして使えばすぐなんだろうと思います。
が、続きを書きます。興味ある人は読んでね!

Django-nonrelを使ってGAEでOpenID認証をする方法を探してて、gaeopenidというのを見つけて、それで実装していたんですが、どうもそれは以前のDjangoをGAEで使う場合のやつみたいで、今はdjango_openid_authを普通に使えるっぽい?という情報を見た。まぁ設定はしないといけないんだけども。

Managing per-field indexes on App Engine

上記のサイトでは、Django-nonrelでdjango_openid_authを使うとOpenIDで使うフィールドがデータストアでindex貼られてないからデータが取れないからそういう設定いるよという話だった。まずこれを見て、django_openid_authが使えるっぽいという確証を持った訳です。

次に、先のURLでリンクが貼られていますが、django_openid_authのサイトからライブラリをダウンロードしてきます。
これを解凍して、django_openid_authパッケージをプロジェクトにペーストします。

README.txtを見たら、OpenID用のライブラリをインストールしろと書いてあるので、以下のURLから、Python用のOpenIDのライブラリをダウンロードして、openidパッケージをプロジェクトにペーストします。
http://www.janrain.com/openid-enabled

ライブラリの準備はとりあえずこれで完了です。次に、GAE用のファイルの作成などしていきます。
settings.pyの編集を行います。まずはINSTALLED_APPSです。

INSTALLED_APPS = (
#    'django.contrib.admin',
    'django.contrib.contenttypes',
    'django.contrib.auth',
    'django.contrib.sessions',
    'djangotoolbox',
    'autoload',
    'dbindexer',

    # djangoappengine should come last, so it can override a few manage.py commands
    'djangoappengine',

    # django_openid_auth
    'django_openid_auth',
)

次に、OpenId用の設定です。同じくsettings.pyです。最後に追加すればいいかと。設定は各自の都合で見直していただければと思いますが、とりあえずこのままでも大丈夫ではないかと思います。

# django_openid_auth
AUTHENTICATION_BACKENDS = (
    'django_openid_auth.auth.OpenIDBackend',
    'django.contrib.auth.backends.ModelBackend',
)
OPENID_CREATE_USERS = True
OPENID_UPDATE_DETAILS_FROM_SREG = True
OPENID_USE_AS_ADMIN_LOGIN = False
LOGIN_URL = '/openid/login/'
LOGIN_REDIRECT_URL = '/'
GAE_SETTINGS_MODULES = (
    'gae_openid_settings',
)

次に、gae_openid_settings.pyを作成します。これは、OpenIDライブラリで保存されるデータが、データストアでindexが貼られていないため、indexを設定する処理です。GAEではindexのないデータに対して比較ができないためです(たぶん)。この投稿の先頭のほうで説明したやつです。

from django_openid_auth.models import Association, UserOpenID

FIELD_INDEXES = {
    Association: {'indexed': ['server_url', 'assoc_type']},
    UserOpenID: {'indexed': ['claimed_id']},
}

長いですが、次です。urls.pyを修正します。/openid/以下がOpenID認証を行うURLにします。

urlpatterns = patterns('',
    url(r'^$', 'django.views.generic.simple.direct_to_template', {'template': 'home.html'}),
    url(r'^_ah/warmup$', 'djangoappengine.views.warmup'),
    # openid
    (r'^openid/', include('django_openid_auth.urls')),
)

えーと、たぶんこれで大丈夫なはず…。
./manage.py runserver
でDjangoのテストサーバを立ち上げて、 http://localhost:8000/openid/login にアクセスしてみましょう。
以下のような画面になればOKです。

あとは入力フィールドにOpenID用のurlを入力しましょう。私は入力欄の場合は
http://www.yahoo.co.jp
をよく使います。これでYahoo!Japanのアカウントでログインできます。

できたら、http://localhost:8000/にリダイレクトされます。このままだと本当にログインできているかどうか、さっぱりわかりませんが、とりあえずできているようです。確認方法はまた別記事にしようと思います。実はまだログアウトする方法がわかっておらんのですorzわかりました。Djangoの認証のログアウトを行うだけでした。

その2に続く・・・


Python:Django-nonrel on GAEでユーザ作成

Django-nonrelでGAEにデプロイしたときに、INSTALLED_APPSにdjango.contrib.authがあるからだと思うのですが、superuserを作ってくれと言われます。そこで入力すると、エラーが発生して作れませんでした。デプロイ自体はうまくいってるんでいいんですが。

ググって見つけられやすいようにエラーの内容を貼っときます。

Exception exceptions.AttributeError: “‘NoneType’ object has no attribute ‘mkstemp'” in <bound method DatastoreFileStub.__del__ of <google.appengine.api.datastore_file_stub.DatastoreFileStub object at 0x10e198d90>> ignored

でも管理者が作れないということは、どうしたらいいんだろうか?と思ってちょっとググってみたところ、解決方法が書いてありました。

参考URL:
django-nonrel on GAE – joinwithjack

Django-nonrelには、GAE用にカスタマイズされたmanage.pyがあるので、それのコマンドを使います。remoteというコマンドに続けて実行したいコマンドを入力します。ここでは、createsuperuserです。

python manage.py remote createsuperuser

入力するとGAE側でユーザを作る処理が走って、ユーザの情報を聞かれるので、入力していきます。すると、普通にユーザを作ることが出来ました。ダッシュボードのDatastore Viewerから確認して、めでたしめでたし。


日記:通勤用自転車サーチを作成しました。

勉強していたPythonとDjangoとjQueryとCSS3を使って、GAE上に通勤用自転車サーチというサービスを作りました。まぁ別に通勤用自転車サーチという名前にしているけれど、特にそんなこともないです。単に自転車のジャンルを絞って検索できるようにしているだけなんですけれどね。

http://319ring.appspot.com

バーッと一気に流し読みできるから、一個一個の商品詳細を見なくてもいいというだけでも、自転車の特徴を知るのにいいんじゃないかなと思います。

自分が気になっている自転車の比較サイトみたいな感じにできたら面白いかなと思うので、そういう機能を今後付けていってみようと思います。ひとまずはリリースと。画像とか一切使ってないし作ってないので、製作時間は3日くらい。1ページのペラでこんなにかかっていてはいかんけれど、Python, Django, jQueryの勉強になったっす!


Django:jQuery-tmplとの競合を回避する

競合を回避・・・できてないような気もしますが。

jQueryにも本家が作っているテンプレートエンジンがあって、それがjQuery-tmplというらしいです。さっき知ったのですが。いちいちJavaScriptでhtmlを組み立てるのは面倒いので、テンプレートエンジンないかなと思って調べたら、本家が作ってたと。さすが。

そのjQuery-tmplのタグですが、${var}みたいな感じで使えます。jQueryでおなじみの方式ですね。でもこのテンプレートエンジンの中で条件分岐とかループとかできんのかいな?と思っていたら、できるようです。
{{if var == 1}}
${var}
{{/if}}
みたいな感じ。

しかしですよ。{{ }}というデリミタは、Djangoの変数用のデリミタなのです。
普通にhtml内で使うと、Djangoのデリミタとして認識してしまうから使い物になりませんorz

Djangoで、文字列として{{ }}を出力できたら問題ないじゃないか!と思って調べたところ、これらの文字自体を出力する方法は、

<ul id="results"></ul>
<scrpit id="itemTemplate" type="text/x-jquery-tmpl">
{% templatetag openvariable %}each items{% templatetag closevariable %}
<li>${name} - ${price}</li>
{% templatetag openvariable %}/each{% templatetag closevariable %}
</script>

という形になります。{{という2文字が{% templatetag openvariable %}という、ものすごく冗長な書き方になるのはちょっとしんどいですが、滅多に登場しないであろうと考えて…。いや、登場するか…。