Django基本チュートリアル2 掲示板を作る1

February 07, 2009 at 11:01 AM | View Comments

ueBLOG | Djangoの基本チュートリアル

の続きです。

次に簡単な掲示板を作ってみます。

アプリの名前はbbsとします。

firstprojectのディレクトリに移動して

% python manage.py startapp bbs

でアプリを作ります。

startproject/settings.py のINSTALLED_APPSに firstproject.bbsを追加します。

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'firstproject.firstapp',
    'firstproject.bbs', #追加
)

次にモデルを作ります、。

firstproject/bbs/models.py

from django.db import models

class Entry(models.Model):

    author = models.CharField(max_length=50)
    body = models.TextField()
    updated = models.DateTimeField(auto_now=True)

    def __unicode__(self):
        return self.author

authorが投稿者、bodyが掲示板に書き込みをした内容、updatedは投稿、編集した時刻です。

updatedのauto_now=Trueは、何もしなくても新規や変更のあったときに時刻を記録してくれるタイムスタンプです。

__unicode__はこのモデルのインスタンスを表示するときに返す文字列です。ここではauthorを返すようにしました。

次にmodelsをデータベースに反映させます。

はじめてsyncdbをする場合はINSTALLED_APPSに登録しているアプリのモデルすべてを反映します。

% python manage.py syncdb
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
# bbsアプリのEntryモデルを作る
Creating table bbs_entry
# スーパーユーザーを作るか聞かれます。username admin password adminで作っておきましょう。後で修正できます。
You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username: admin
E-mail address: #あなたのメールアドレス
Password: #admin
Password (again): #admin
Superuser created successfully.
Installing index for auth.Permission model
Installing index for auth.Message model

これでbbs_entryというテーブルが出来ました。

では、どんなSQLでテーブルが作られたか見てみましょう。

% python manage.py sql bbs
BEGIN;
CREATE TABLE "bbs_entry" (
    "id" integer NOT NULL PRIMARY KEY,
    "author" varchar(50) NOT NULL,
    "body" text NOT NULL,
    "updated" datetime NOT NULL
)
;
COMMIT;

id は自動で振られる値でPRIMARY KEYになります。

Djangoは manage.py shellでPyhonの対話式でアプリを試すモードがあるので、これをつかって今つくったモデルを扱ってみましょう。

% python manage.py shell
Python 2.5.4 (r254:67916, Dec 23 2008, 15:10:54) [MSC v.1310 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)

# bbs/models.pyのEntryクラスをインポート
>>> from bbs.models import Entry
# id と updatedは自動で入るのでauthorとbodyのみ引数にしてEntryクラスのインスタンスを作ります。
>>> e = Entry(author="John", body="first entry")
#どんな値があるのか見てみましょう。idとupdatedの値はまだなく、データベースにも反映されてません。
>>> e.__dict__
{'body': 'first entry', 'updated': None, 'id': None, 'author': 'John'}
#saveメソッドを呼び出すことでデータベースに反映されます。
>>> e.save()
>>> e.__dict__
{'body': 'first entry', 'updated': datetime.datetime(2009, 2, 6, 20, 20, 30, 562000), 'id': 1, 'author': 'John'}
# 次にデータベースからすべてのデータを取得してみましょう
>>> entrys = Entry.objects.all()
>>> entrys
[<Entry: John>]
# id(primarykey)が1のもの(先ほど作ったデータ)を取得
>>> e1 = Entry.objects.get(pk=1)
>>> e1.__dict__
{'body': u'first entry', 'updated': datetime.datetime(2009, 2, 6, 20, 20, 30, 562000), 'id': 1, 'author': u'John'}
# id,author,body,updateなどは属性になっています。
>>> e1.body
u'first entry'
>>> e1.updated
datetime.datetime(2009, 2, 6, 20, 20, 30, 562000)
# インスタンスのdeleteメソッドでデータが消えます。
>>> e1.delete()
# 消えたか確認
>>> Entry.objects.all()
[]
>>> Entry.objects.count()
0
#テスト用にデータを作っておく
>>> e = Entry(author="John", body="body1")
>>> e.save()
>>> e = Entry(author="Paul", body="body2")
>>> e.save()
>>> Entry.objects.all()
[<Entry: John>, <Entry: Paul>]

と、このようにデータを扱うことができます。

より詳しくモデルの扱いを知りたい場合はドキュメントを参考にしましょう。

クエリを生成する — Django v1.0 documentation

では、今つくったエントリーを実際にブラウザで表示してみましょう。

URLの振り分けはfirstproject/urls.py 表示する部分を作るのはfirstproject/bbs/views.py、

になります

firstproject/urls.py

from django.conf.urls.defaults import *
urlpatterns = patterns('',
     url(r'^$', 'firstproject.firstapp.views.helloworld', name='home'),
     (r'^ut/$', 'firstproject.firstapp.views.usetemplate'),
     (r'^plus/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'firstproject.firstapp.views.calc',
            {"templatename": "calc.html"}),
     (r'^bbs/$', 'firstproject.bbs.views.object_list'), #これを追加
)
firstproject/bbs/views.py

from django.shortcuts import render_to_response
from bbs.models import Entry

def object_list(request):
    object_list = Entry.objects.all()
    return render_to_response("object_list.html",
            {
                "object_list": object_list,
            }
    )

テンプレートにobject_list.htmlというファイルを使うように指定したので作ってみましょう。

アプリ直下にtemplatesというディレクトリを作るとDjangoは自動的にtemplatesを探してくれます。

% mkdir bbs/tempates

そしてobject_list.htmlというファイルを作ります。

firstproject/bbs/templates/object_list.html

{% for object in object_list %}
<dl>
    <dt>{{ object.id }} :<font color=green><b>{{ object.author }}</b></font>
    :{{ object.updated|date:"Y-m-d H:i" }}</dt>
    <dd>{{ object.body }}</dd>
</dl>
{% endfor %}

これで

http://static.flickr.com/3529/3258744845_18aceea25c.jpg

のようなものが表示されたと思います。|date:"Y-m-d H:i"はテンプレートタグのフィルターと呼ばれるものです。ここではdatetimeの表示を見やすくしました。

フィルターは他にもいろいろあるのでドキュメントを調べてみてください。

組み込みタグ/フィルタリファレンス — Django v1.0 documentation

さて、表示の部分はできましたが、掲示板なのでユーザーの投稿のフォームが必要です。

Djangoにはモデルから簡単にフォームを作る機能があるのでそれを利用しましょう。

一度、サーバーをとめて、再び対話式を使って実験してみます。

% python manage.py shell
# forms と bbs/models.pyのEntryをインポート
>>> from django import forms
>>> from bbs.models import Entry
# Entryモデルに基づいたEntryFormクラスを作ります。
>>> class EntryForm(forms.ModelForm):
...     class Meta:
...         model = Entry
...
# インスタンス化
>>> f = EntryForm()
# print で htmlが表示されます。
>>> print f
<tr><th><label for="id_author">Author:</label></th><td><input id="id_author" type="text" name="author" maxlength="50" /></td></tr>
<tr><th><label for="id_body">Body:</label></th><td><textarea id="id_body" rows="10" cols="40" name="body"></textarea></td></tr>
# as_pメソッドでpタグくくりにもできます。
>>> print f.as_p()
<p><label for="id_author">Author:</label> <input id="id_author" type="text" name="author" maxlength="50" /></p>
<p><label for="id_body">Body:</label> <textarea id="id_body" rows="10" cols="40" name="body"></textarea></p>
# authorやbodyなど一部のhtmlを表示させたいときは辞書のように指定します。
>>> print f["author"]
<input id="id_author" type="text" name="author" maxlength="50" />
# ユーザーからのデータ入力は辞書で値を渡します
>>> f = EntryForm({"author":"Link"})
# authorだけでbodyを入力しませんでした。そうするとerrorsにエラーメッセージが辞書で渡されます。
>>> f.errors
{'body': [u'This field is required.']}
# is_validメソッドは正しいデータがあるか確認するメソッド
>>> f.is_valid()
False
# 今度はauthorもbodyも入力
>>> f = EntryForm({"author":"Link", "body":"new entry"})
# errorsは空で、is_validメソッドはTrueを返します
>>> f.errors
{}
>>> f.is_valid()
True
# フォームインスタンスをsaveすることでデータベースに反映することもできます
>>> f.save()
<Entry: Link>
>>> Entry.objects.all()
[<Entry: John>, <Entry: Paul>, <Entry: Link>]
>>>

では、上の結果をふまえてフォーム部分を作ります。

まず、firstproject/bbs/forms.pyというファイルを作ってEntryFormクラスを書きましょう。

from django import forms

from bbs.models import Entry

class EntryForm(forms.ModelForm):

    class Meta:
        model = Entry

次にformを表示させるためにfirstproject/bbs/views.pyを編集します。

from django.shortcuts import render_to_response

from bbs.models import Entry
from bbs.forms import EntryForm

def object_list(request):
    if request.method == "POST": #requestがPOSTかGETか判別
        form = EntryForm(request.POST) #request.POSTは辞書のようなデータなのでそのままEntryFormに渡せる
        if form.is_valid(): # エラーがないか判別
            form.save() #エラーがないのでsave()
            form = EntryForm() #保存したのでフォームを空に、is_valid()でFalseの場合はerrosや入れられた値を持ったまま
    else:
         form = EntryForm() #GETなので空のフォームを作る
    object_list = Entry.objects.all()
    # formとobject_listをテンプレートに渡す
    return render_to_response("object_list.html",
            {
                "form": form,
                "object_list": object_list,
            }
    )

慣れないと複雑に見えるかもしれませんが、すぐになれると思います。

次にテンプレートにフォームの部分を追加します。

firstproject/bbs/templates/object_list.html

{% for object in object_list %}
    <dl>
        <dt>{{ object.id }} :<font color=green><b>{{ object.author }}</b></font>
        :{{ object.updated|date:"Y-m-d H:i" }}</dt>
        <dd>{{ object.body }}</dd>
    </dl>
{% endfor %}
<form method="POST" action="." >
    <table>
        {{ form }}
    </table>
    <input type="submit" />
</form>

これで非常に簡単ですが掲示板ができました。

http://static.flickr.com/3495/3259576456_179b583029.jpg

authorとbodyどちらかしか入力しないでsubmitボタンを押すと、エラーメッセージがでると思います。

categories: django
Read and Post Comments

Djangoの基本チュートリアル

January 19, 2009 at 11:11 PM | View Comments

今回は、Djangoをインストールしたばかりの人を想定した、簡単なDjangoの説明をしたいと思います。

今回はWindowsで行います。

コマンドプロンプトを立ち上げて、プロジェクトを作りたいディレクトリに移動します。

今回は、C:/tmp/django_firstというディレクトリで行いますが、好きなディレクトリで行ってください。

まず「firstproject」というプロジェクトを django-admin.py startproject firstproject で作ります。

django-admin.pyはdjangoのインストール先のdjango/binにあります。windowsの場合は以下の場所にあると思います。

C:/tmp/django_first>python c:/Python25/Lib/site-packages/django/bin/django-admin.py startproject firstproject

#firstprojectのディレクトリに移動して中身を確認します


C:/tmp/django_first/firstproject>dir
ドライブ C のボリューム ラベルがありません。
ボリューム シリアル番号は 30E0-5C63 です

C:/tmp/django_first/firstproject のディレクトリ

2009/01/19  23:31    <DIR>          .
2009/01/19  23:31    <DIR>          ..
2009/01/19  23:31             2,864 settings.py
2009/01/19  23:31                 0 __init__.py
2009/01/19  23:31               557 manage.py
2009/01/19  23:31               566 urls.py

と、このように4つのファイルができました。

  • settings.py 設定ファイル
  • manage.py django-admin.pyの代わりにテストサーバーを動かしたり、モデルからDBのテーブルを作ったりテストを動かしたりいろいろします。
  • urls.py アクセスされたURLを実際の処理に振り分ける
  • __init__.py ディレクトリ構造をPythonのパッケージにするためのファイル

では、ここでテストサーバーを動かしてみましょう

It worked

という画面が表示されればOKです。しかし、この時点では何もできないのでコマンドプロンプトから

「Ctrl + c」キーを同時押しをして終了させましょう。

つぎにアプリを作ります。Djangoはプロジェクトの中に複数のアプリを作ることができます。

アプリを作るのは

C:/tmp/django_first/firstproject>python manage.py startapp firstapp

# できたファイルの中身を確認する

C:/tmp/django_first/firstproject>dir firstapp

C:/tmp/django_first/firstproject/firstapp のディレクトリ

2009/01/19  23:54    <DIR>          .
2009/01/19  23:54    <DIR>          ..
2009/01/19  23:54                60 models.py
2009/01/19  23:54                 0 __init__.py
2009/01/19  23:54                27 views.py
               3 個のファイル                  87 バイト
               2 個のディレクトリ  75,211,472,896 バイトの空き領域

と、アプリの雛形である

  • models.py モデルを作るところ
  • views.py 表示する部分を作るところ
  • __init__.py ディレクトリ構造をPythonのパッケージとして認識させるために必要

ができました。

プロジェクトにfirstappをアプリとして認識させるためにfirstproject/settings.pyの編集をします。

INSTALLED_APPSにfirstproject.firstappを追加すればOKです。

#firstapp/settings.py
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    # 以下を追加
    'firstproject.firstapp',
)

さて、このプロジェクトfirstprojectとアプリで、いろいろ表示をしていこうかと思うのですが、まずはHelloWorldの表示からやってみたいと思います。

http://localhost:8001/ でアクセスされたときに「Hello World」と表示するものです。

まずはurls.pyの編集です。

#firstproject/urls.py

from django.conf.urls.defaults import *
urlpatterns = patterns('',
     (r'^$', 'firstproject.firstapp.views.helloworld'),
)

これはhttp://localhost:8001/でアクセスされたときにfirstapp/views.py の helloworld関数に表示処理を任せるという意味です。

では firstproject/firstapp/views.pyにhelloworld関数を作ってみましょう。

from django.http import HttpResponse

def helloworld(request):
    return HttpResponse("Hello World")

urls.pyから処理を任される関数はviews関数といって2つ決まりがあります。

  • 第一引数にdjango.http.HttpRequestオブジェクトのrequestをとる
  • django.http.HttpResponseオブジェクトをreturnする

HttpResponse生成の一番簡単は方法は文字列を引数に渡すことなので、これで

C:/tmp/django_first/firstproject>python manage.py runserver 0.0.0.0:8001

でテストサーバーを動かして、ブラウザでhttp://localhost:8001/にアクセスするとブラウザに

Hello World

と表示されたと思います。

これで一応表示までできましたが、HttpResponse("<html><head>~~~")のようにhtmlを書くわけにはいかないので、Djangoではテンプレートというものを作ります。

http://localhost:8001/ut/

とアクセスされたら、テンプレートを使った表示を返すようにしてみようと思います。

まずはfirstproject/urls.pyから

urlpatterns = patterns('',
     (r'^$', 'firstproject.firstapp.views.helloworld'),
     (r'^ut/$', 'firstproject.firstapp.views.usetemplate'), #ここを追加。
)

ulrs.pyから渡す関数名はusetemplateという名前にしました。さっそくfirstproject/firstapp/views.pyに追加しましょう。

from django.http import HttpResponse
from django.shortcuts import render_to_response #追加

def helloworld(request):
    return HttpResponse("Hello World")

def usetemplate(request): #以下追加
    retrun render_to_response('ut.html',
        {
            "hensu1": "HENSU1",
            "hensu2": "HENSU2"
        }
    )

テンプレートを使うにはいろいろ方法があるのですが、django.shortcuts.render_to_responseが良く使われます。これは第一引数にtemplateの名前を入れて、第二引数に{変数名:値}といった辞書を渡すことでHttpResponseオブジェクトを作ります。

今回はut.htmlという名前のテンプレートを作ることにしました。これをどこに置いたらいいかというと、settings.pyのTEMPLATE_DIRSに置く場所を設定することもできますが、djnagoはアプリの下に「templates」というディレクトリがあったら、そのディレクトリにテンプレートがあるか自動的に探してくれます。

なので、今回はfirstproject/firstapp/templates/ut.htmlというファイルを作りましょう。

C:/tmp/django_first/firstproject>mkdir firstapp/templates

をしてから、今回は簡単ですが以下のようなut.htmlを作ってみました。

<html>
       <head>
               <title>First Django</title>
       </head>
       <body>
               {{ hensu1 }} <br />
               {{ hensu2 }} <br />
       </body>

</html>

さきほどのviews関数でhensu1とhensu2という名前の辞書を作りましたので、その値「HENSU1」「HENSU2」が表示されればOKです。

うまく表示されたでしょうか??

ではこのまま、このテンプレートを使ってリストなどのシーケンシャル(連続した)なオブジェクトや、値が辞書のものを扱ってみたいと思います。

firstproject/firstapp/views.py

from django.http import HttpResponse
from django.shortcuts import render_to_response

def helloworld(request):
    return HttpResponse("Hello World")

def usetemplate(request):
    return render_to_response('ut.html',
        {
           "hensu1": "Hensu1",
           "hensu2": "Hensu2",
           "lis": ["spam", "ham", "egg"], #追加
           "dic": {"name": "value"}, #追加
        }
    )

これらを扱うfirstproject/firstapp/templates/ut.htmlを以下のように追加しました。

<html>
       <head>
               <title>First Django</title>
       </head>
       <body>
               {{ hensu1 }} <br />
               {{ hensu2 }} <br />
               <p>
               LIST
               <ul>
               {% for i in lis %}
                     <li>{{ i }} </li>
               {% endfor %}
               </ul>
               </p>
               <p>
               Dictonary<br />
               dic["name"] = {{ dic.name }}
               </p>
       </body>
</html>

これで、リストや辞書がつかるようになったと思います

(後で図を入れる)

{% -- %}がテンプレートの関数的なものを扱い

{{ -- }} が値を表示する

と思ってもらえればOKです。

そして辞書のdic["name"]をあらわすのは {[ dic.name }} だということにも注目してください。

さて、次はクラスです。

firstproject/firstapp/views.pyを以下のように変更しました。Personというクラスを作ってpeopleという名前の値にPersonで作ったインスタンスのリストを入れました。

from django.shortcuts import render_to_response

class Person(object):

    def __init__(self, name, age):
        self.name = name
        self.age = age

def helloworld(request):
    return HttpResponse("Hello World")

def usetemplate(request):
    return render_to_response('ut.html',
        {
           "hensu1": "Hensu1",
           "hensu2": "Hensu2",
           "lis": ["spam", "ham", "egg"],
           "dic": {"name": "value"}
           "people": [
               Person("John", 34),
               Person("Paul", 33),
           ]
        }
    )

対するテンプレートは以下のようにしました。

firstproject/firstapp/templates/ut.html

<html>
       <head>
               <title>First Django</title>
       </head>
       <body>
               {{ hensu1 }} <br />
               {{ hensu2 }} <br />
               <p>
               LIST
               <ul>
               {% for i in lis %}
                     <li>{{ i }} </li>
               {% endfor %}
               </ul>
               </p>
               <p>
               Dictonary<br />
               dic["name"] = {{ dic.name }}
               </p>
               <p>
               class
               <ul>
               {% for person in people %}
                    <li>Name: {{ person.name }} Age: {{ person.age }}</li>
               {% endfor %}
               </ul>
               </p>
       </body>
</html>

こちらは {{ person.name }} と {{ person.age }} とそのままですね。

これで表示されたと思います(あとで図)

さて、次はurlsについてもう少し説明を加えたいと思います。

Webアプリは、アクセスされるURLそのものを使って処理を分けたいときがあります。

たとえばBLOGなどでは

http://localhost:8001/blog/2008/12/24/

などでアクセスすると 2008年12月24日のエントリーを表示するように設計することなどがあるでしょう。

このチュートリアルでBLOGを扱うのは大変なので、

http://localhost:8001/plus/33/44/

をすると33 + 44 の結果 77 を返すようなサンプルを作りましょう。

まず、firstproject/urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns('',
     (r'^$', 'firstproject.firstapp.views.helloworld'),
     (r'^ut/$', 'firstproject.firstapp.views.usetemplate'),
     (r'^plus/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'firstproject.firstapp.views.calc', #追加
         {"templatename":"calc.html"}),
)

さて、この(?P<arg1>¥d+) はpythonの名前つき正規表現の書き方です。これで括弧ないのパターン(この場合は数値)にマッチしたものにarg1という変数に入れてくれます。

なので、

http://localhost:8001/plus/33/44/

の場合、arg1=33, arg2=44 というように切り出しをします。そしてDjangoは続くviews関数(firstproject/firstapp/views.py)の引数に名前付き引数として渡します。

そして、タプルの3つ目の {"templatename":"calc.html"} という辞書も、templatename="calc.html"という名前付き引数としてviews関数に渡します。

firstproject/firstapp/views.py は

from django.http import HttpResponse, Http404
from django.shortcuts import render_to_response

#途中省略
def calc(request, arg1, arg2, templatename='calc.html'):
    try:
        arg1 = int(arg1)
        arg2 = int(arg2)
    except:
        raise Http404
    result = arg1 + arg2
    return render_to_response(templatename,
           {
               "arg1": arg1,
               "arg2": arg2,
               "result": result
           }
    )

とします、views関数の決まりは * 第一引数にHttpRequestオブジェクトのrequestをとる * HttpResponseオブジェクトをreturnする のが決まりなので、

calc関数の第一引数にrequest、

そして、続く引数はurls.pyから渡された、arg1, arg2, templatenameになります。

注意点は、整数として正規表現でマッチしたarg1, arg2ともに文字列として渡されている点です。

なのでtry構文の中で int(arg1), int(arg2) と型のキャストを行っています。さらにここでエラーが起きた場合は404エラーを返す raise Http404を発生させています。

そして、firstproject/firstapp/templates/calc.htmlです

<html>
    <title>CALC</title>
    <body>
           {{ arg1 }} + {{ arg2 }} = {{ result }}
    </body>
</html>

これは、もう説明するまでもないですね。

(図をいれる)

次にurlの逆引きです。

<明日、また続き>

次はテンプレートの敬称です。

<あとで書く>

さて、最後はmodelとformです。

categories: django
Read and Post Comments

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の基本を一回間に入れようかと思います。

categories: django-handson6, django
Read and Post Comments

PinaxでDjango入門1 complete_projectとbasic_projectを動かす

January 17, 2009 at 12:02 AM | View Comments

先日のDjango勉強会Disc.6のフォローアップ第1回です。

  • Python2.5
  • django1.0
  • PIL
  • pinax (svnのtrunk)

で行っています。

まずpinaxのインストールは

% svn checkout https://svn.pinaxproject.com/pinax/trunk/ pinax

でsubversionを使ってインストールしてください。

そして問題のPILのが必要なのですが、インストールは前回のエントリーを参考にしてください。

ueBLOG | Python Image Libraryのインストール

まず、今回はcomplete_projectとbasic_projectを動かすところまでやってみたいと思います。

% cd projects/complete_project
% python manage.py syncdb
% python manage.py runserver 0.0.0.0:8001

のみでアプリが動きます。

スクリーンキャストを撮ったので、pinaxがどんなものか知りたい方は参考にしてください。

http://www.ueblog.org/media/static/django6_1/django6_1.swf

ではsettings.pyを読んでみましょう。このファイルは文字通り設定ファイルになります。

まずデータベースの定義です。

DATABASE_ENGINE = 'sqlite3'    # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'ado_mssql'.
DATABASE_NAME = 'dev.db'       # Or path to database file if using sqlite3.

pinaxではsqlite3を使い、プロジェクト直下のdev.dbというファイルにデータベースを作るようになっています。

これは、とりあえずすぐに動かせるように設定してあるので、本番環境ではMySQLやpostgresqlなどが良いと思います。

次にINSTALLED_APPS、complete_projectでは、これだけのアプリがインストールされるように定義されています。

INSTALLED_APPS = (
    # included
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.humanize',
    'django.contrib.markup',

    # external
    'notification', # must be first
    'django_openid',
    'emailconfirmation',
    'django_extensions',
    'robots',
    'dbtemplates',
    'friends',
    'mailer',
    'messages',
    'announcements',
    'oembed',
    'djangodblog',
    'pagination',
#    'gravatar',
    'threadedcomments',
    'wiki',
    'swaps',
    'timezones',
    'feedutil',
    'app_plugins',
    'voting',
    'tagging',
    'bookmarks',
    'blog',
    'ajax_validation',
    'photologue',
    'avatar',
    'things',
    'flag',
    'schedule',
    'microblogging',
    'locations',

    # internal (for now)
    'analytics',
    'profiles',
    'account',
    'tribes',
    'projects',
    'misc',
    'photos',
    'tag_app',

    'django.contrib.admin',
)
% python manage.py syncdb

をしたときに、これらのアプリのmodelを探して、その定義に沿ったtableを設定されたDBに作ります。

Webフレームワークの多くはデータベースのtableを定義が先で、それに沿ったmodelをフレームワーク側が作ることが多いようですが、Djangoは逆なのでデータベースの知識がそれほどなくても複雑なモデルを組上げることができます。

そして

% python manage.py runserver

でテストサーバーを動かすことができます。

Djangoを動かす環境とブラウザを動かす環境が違う場合は動画でやったように

% python manage.py runserver 0.0.0.0:8001

サーバーのIPアドレスと8001ポートを使用してテストサーバーが動きます。

0.0.0.0と指定しておくと、そのサーバーのIPアドレスで立ち上がるので便利です。

では、次回は

basic_projectのprofile(basic_profile)をcomplete_projectのものに変更する

をやることによって、

  • アプリを追加したときのsettings.pyの編集
  • urls.pyとurl関数の説明
  • templateの読み込みのルール

を説明してきたいと思います。

categories: pinax, django-handson6, django
Read and Post Comments

Djangoの学習にPinaxはどうだろう?

December 30, 2008 at 06:00 PM | View Comments

Djangoの学習に何がいいかと思っていたけど

Pinax はなかなか良い気がする。

これはDjangoのよく聞くPluginアプリを集めたもので、

「アホか!」

というほど、いろいろなモノが入っている。

pinax/apps/external_apps

には

  • dbtemplates
  • feedutil
  • messages
  • photologue
  • things
  • ajax_validation
  • django_extensions
  • flag
  • microblogging
  • robots
  • threadedcomments
  • announcements
  • django_openid
  • friends
  • notification
  • schedule
  • timezones
  • app_plugins
  • djangodblog
  • gravatar
  • oembed
  • voting
  • avatar
  • djangologging
  • locations
  • omb
  • swaps
  • wiki
  • bookmarks
  • emailconfirmation
  • mailer
  • pagination
  • tagging

などがあった。

一方、それらを動かすモジュールも必要ですが、

pinax/libs/external_libs

を見ると

  • Creoleparser-0.5.0
  • Genshi-0.5.1
  • elementtree-1.2.6
  • Pygments-0.11.1
  • dateutil
  • docutils-0.4
  • feedparser-4.1
  • gdata.py-1.0.13
  • geopy-trunk
  • markdown-1.7
  • misc
  • python-openid-2.1.1
  • python-twitter-0.5
  • python-yadis-1.1.0
  • pytz-2008b
  • simplejson-1.9.1
  • textile-2.0.11
  • vobject-0.6.6
  • ybrowsera

など、Pythonで良く使うモノが入っている。

manage.pyを拡張していて、これらをSite.addsitedirを使って動的に追加しているので、

いちいち足りないモジュールをeasy_installなどで追加することなくすぐに使えるようだ。

しかし、PILなどpythonオンリーじゃないモジュールやライブラリは当然、別途インストール。

svn checkout http://svn.pinaxproject.com/pinax/trunk/ pinax

でtrunk版を落として

その後は、面倒なsettings.pyなどの設定なしに

% cd pinax
% cd projects/complete_project/
% python manage.py syncdb
% python manage.py runserver

で、豊富なアプリがいきなり動く。

ちょっとだけ試してみたい人には非常にうれしい。

もちろんローカル環境でなく公開するなら、それなりの設定は必要。

しかし、

pinax/projects/complete_project/deploy

  • complete_project.fcgi
  • complete_project.wsgi
  • modpython.py

などが存在しているし、ドキュメントにも詳しく書かれているので本番で使うのにあちこち調べまくる心配はなさそうだ。

Pluginを使ったときに、どのようなファイルの構成などをどうするか解説してあるBLOGや本はあまりないと思うので、

pinaxの構成や仕組みを参考にするとよいかもしれない。

Pythonのプロジェクトだけあって、ドキュメントもtrunk版のものがちゃんある。

Installation ? Pinax v0.7.0dev documentation

ただし、それぞれのPluginアプリは、それらについているドキュメントのようだ。

さて、pinaxを使用し、実際に稼動しているWebサービスには

などがある、

どのサービスもユーザー視点で見ると微妙な感じだが、どうやって実装しているか考えながら眺めると面白い。

まだ、ちょっと動かしてみただけだけど、もし使えそうなら

いろいろやってみようかと思う。

ちなみに、syncdb、runserverで動かした直後のpinaxの画面

ログイン画面

http://static.flickr.com/3121/3150462012_94390dbb38.jpg

ログイン直後の個人設定の画面

http://static.flickr.com/3264/3149629759_e16cf6660e.jpg

ユーザーごとにタイムゾーンを選択可能

http://static.flickr.com/3266/3149629795_1f04824dda.jpg

友達の招待も実装済み

http://static.flickr.com/3259/3150462104_0e8628689f.jpg

ちなみに管理画面(クリックでオリジナルサイズ)

http://static.flickr.com/3217/3150474772_f250a1a43e.jpg
categories: django
Read and Post Comments

Django 勉強会 Disc.6やります

November 22, 2008 at 11:01 AM | View Comments

「やろう!」とだけ言いっぱなしだった「Django勉強会」ですが、

everes親分とVoluntasさんが仕切ってくれたおかげで

開催がほぼ確定になったようです!!

Django 勉強会 Disc.6(ほぼ確定) - Twisted Mind

  • 日時: 2009/1/15 (懇親会あり) 18:30 - 21:00
  • 懇親会: 21:30 - 23:00 参加費は 4000 円前後にしたい
  • 場所: 株式会社アクセンス・テクノロジー 会議室 (神楽坂にあります)

僕も、

  • Django を使うための最低限の Python の知識を学ぶ
  • Django を触って何かを作る

あたりで講師みたいのをする予定なので、ちょっとネタをいろいろ考えないと、、、、

最近EBCDICがどうのとか、packed-decimalの変換とか

面白くないことばかりやっているので、何がいいか考えよう。

categories: django
Read and Post Comments

Google Developer Day2008

June 11, 2008 at 10:06 AM | View Comments

今日はGoolge developer day2008に行きました。

http://farm4.static.flickr.com/3015/2566861476_40ae70f212_m.jpg

午後からはGoogle app engineのHackathonに参加。

Image APIが登場してから、しばらくたちますが、サンプルのものはDjangoを使ったものではないので Djangoだけをつかってやってみたかったのですが、変なところでハマりまくって時間内に動くものができませんでした。 だめだなぁ。

途中からはOS-X10.5にPIL (Python Image Library)をインストールするのに四苦八苦。OS-XはIPythonとかPILとか入った状態で出荷してほしいな。

家に帰ってとりあえず作ったのが以下

非常に単純なのでアレですが、一応公開してみます。

http://farm4.static.flickr.com/3061/2568643513_4e330552cf.jpg?v=0

/app.yaml

application: ueblog33
version: 1
runtime: python
api_version: 1

handlers:

- url: /photo/.*
  script: main.py

/index.yaml

自動生成

/django

開発版のdjangoをそのまま

/main.py

import os
import sys

# Google App Engine imports.
from google.appengine.ext.webapp import util
# Remove the standard version of Django.
for k in [k for k in sys.modules if k.startswith('django')]:
  del sys.modules[k]
# Force sys.path to have our own directory first, in case we want to import
# from it.
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))

# Must set this env var *before* importing any part of Django
os.environ['DJANGO_SETTINGS_MODULE'] = 'photoapp.settings'

# Import the part of Django we need.
import django.core.handlers.wsgi
import django.core.signals
import django.db
import django.dispatch.dispatcher

# Unregister the rollback event handler
django.dispatch.dispatcher.disconnect(
  django.db._rollback_on_exception,
  django.core.signals.got_request_exception)

# Log errors
#django.dispatch.dispatcher.connect(
#    log_exception,
#    django.core.signals.got_request_exception)

def main():
  # Create a Django application for WSGI.
  application = django.core.handlers.wsgi.WSGIHandler()

  # Run the WSGI CGI handler with that application
  util.run_wsgi_app(application)

if __name__ == '__main__':
  main()

誰か公開していたものをそのまま流用

/photoapp

(python ./django/bin/django-admin.py startproject photoappで作成)

/photoapp/__ini__.py

自動生成

/photoapp/manage.py

自動生成

/photoapp/settings.py

DEBUG = True
TEMPLATE_DEBUG = DEBUG

ADMINS = (
    # ('Your Name', 'your_email@domain.com'),
)

MANAGERS = ADMINS
TIME_ZONE = 'Tokyo/Asia'
LANGUAGE_CODE = 'ja'
SITE_ID = 1
USE_I18N = True
MEDIA_ROOT = ''
MEDIA_URL = ''
ADMIN_MEDIA_PREFIX = '/media/'
SECRET_KEY = '' #自分のを使う
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.load_template_source',
    'django.template.loaders.app_directories.load_template_source',
#     'django.template.loaders.eggs.load_template_source',
)

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    #'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.middleware.doc.XViewMiddleware',
)
ROOT_URLCONF = 'photoapp.urls'
import os
ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
TEMPLATE_DIRS = (
    os.path.join(ROOT_PATH, 'templates'),
)
INSTALLED_APPS = (
    #'django.contrib.auth',
    'django.contrib.contenttypes',
    #'django.contrib.sessions',
    'django.contrib.sites',
    'photoapp.photo'
)
TEMPLATE_CONTEXT_PROCESSORS = (
   #'django.core.context_processors.auth',
   'django.core.context_processors.debug',
   'django.core.context_processors.i18n',
   #'django.core.context_processors.media',
   'django.core.context_processors.request',
)

(自動生成を修正)

/phtoapp/urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^photo/', include('photoapp.photo.urls')),
)

/photoapp/photo

( cd photoapps

python manage.py startapp photo

)

で生成

/photoapp/photo/__init__.py

(自動生成)

/photoapp/photo/urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^$', 'photoapp.photo.views.index'),
    (r'^upload/$', 'photoapp.photo.views.upload'),
    (r'^image/(?P<img_id>\w+)/$', 'photoapp.photo.views.image' ),
)

/photoapp/photo/models.py

from google.appengine.ext import db

class Photo(db.Model):
    photo = db.BlobProperty()
    title = db.StringProperty(required=True)

/photoapp/photo/views.py

from django.http import HttpResponse
from django.shortcuts import render_to_response
from django import newforms as forms
from photoapp.photo.models import Photo

from google.appengine.ext import db
from google.appengine.api import images

class UploadForm(forms.Form):
    title = forms.CharField(max_length=100, min_length=1)
    photo  = forms.FileField(required=True)

def index(request):
    queryset = Photo.all()
    return render_to_response(
        'upload.html', dict(object_list=queryset)
    )

def upload(request):
    if request.method == "POST":
        form = UploadForm(request.POST, request.FILES) #FileField使った場合はrequest.FILESを忘れずに
        if form.is_valid():
            photo = Photo(
                  title=request.POST["title"],
                     )
            photo_object = form.cleaned_data["photo"]
            content = images.resize(photo_object.content,  200, 200) #image apiを使ってリサイズ
            photo.photo = db.Blob(content) #binaryデータはdb.Blobを使う
            photo.put()
            form = UploadForm()
    else:
        form = UploadForm()
    queryset = Photo.all()
    return render_to_response(
        'upload.html', dict(
                    form=form,
                    object_list=queryset)
    )

def image(request, img_id): #Photoインスタンスのkeyを元に画像を表示
    photo = Photo.get(img_id)
    if photo:
        return HttpResponse(photo.photo, content_type="image/jpg")
    else:
        return HttpResponse("NO IMAGE") #本当はNO IMAGEのイメージを出す

/photoapp/photo/templates/upload.html

<html>
<body>
{% if form %}
  <form action="." enctype="multipart/form-data" method="POST"><!-- enctype 忘れないように -->
  {{ form }}
<input type="submit">
</form>
{% endif %}
{% for object in object_list %}
  <div>{{ object.title }}</div>
  <div><img src='/photo/image/{{ object.key }}/'></img></div>

{% endfor %}
</body>
</html>

一応newformsを使ってます。

本来なら画像のファイル形式をみたり、いろいろな画像サイズを作ったりする必要があります。

前半プレゼンだったのですが、思いっきり英語でした。英語できないだめだな〜〜。

WSGIはダブルセスジアイと言ってました ウィスキーって言ってたおれ涙目

あまりにつまらないところでハマったので、知り合い以外と絡むこともなく、

せっかくGoogleの人がいっぱいいたのに何も質問できず、、、、、。

事前準備は重要ですね。

貴重な一日を無駄にしてしまった。。。。

しかし、日本に来てまだ1年半なのに、「この人英語しゃべれるのかな?」と疑問に思うほど日本語が上手な

Ian Lewisさん

http://www.ianlewis.org/index.php/jp/ (日本語)

http://www.ianlewis.org/ (英語)

とも知りあえたし、楽しかったです。

帰りはIanさんと、大谷さんやDjango-jaの人たちと食事して帰りました。

hackasonでもらったTシャツ

http://farm4.static.flickr.com/3172/2569483674_09d79661d6.jpg?v=0
categories: python, googleappengine, django
Read and Post Comments

Google app engineでDjangoを動かす

April 09, 2008 at 10:36 AM | View Comments

まず、普通にGoogle app engineを使えるようにするには秋元さんのエントリーが詳しいのでそちらを参考に

秋元さんはWindowsを使っているがMacの場合はdmgとpkgでインストールする。

ローカル環境であれこれするdev_appserver.pyと、デプロイするためのappcfg.pyはそれぞれ/user/local/bin/に入る

  • /usr/local/bin/dev_appserver.py
  • /usr/local/bin/appcfg.py

またdemoやlibなどのファイルたちは/usr/local/google_appengineにある。

Google app engineはPythonは2.5が必要なのでOS-X10.5以外の人は別途インストールしましょう。Djangoのインストールだがeveresさんの記事が分かりやすいです。

http://www.everes.net/2007/sep/06/install-django-for-begginers-osx/

アプリを作るディレクトリに移ります(どこでもいい)

% cd ~/tmp

/usr/local/google_appengine/libにあるdjangoを使ってプロジェクトを作る(今回はueblog33というアプリ名にしました)

% python /usr/local/google_appengine/lib/django/django/bin/django-admin.py startproject ueblog33

次のように4つのファイルができているはずです。

% ls ueblog33

__init__.py manage.py settings.py urls.py

アプリ直下にdjango自体が必要なのでコピーしましょう

% cp -r /usr/local/google_appengine/lib/django/django ueblog33/django

google app engineを使うためにはueblog33/main.pyを作る必要がある。

http://code.google.com/appengine/articles/django.html

のmain.pyをコピーしてしまいましょう。しかし、#Log errorの後の行はエラーになるので#でコメントアウトしておきましょう。

# Log errors
#django.dispatch.dispatcher.connect(
#    log_exception,
#    django.core.signals.got_request_exception)

次app.yamlをhttp://code.google.com/appengine/articles/django.html を参考につくる

application: ueblog33
version: 1
runtime: python
api_version: 1

handlers:
- url: /static
  static_dir: static

- url: /.*
  script: main.py

静的ファイルを作る場所を指定してあるようなのでstaticディレクトリを作っておきましょう

% mkdir ueblog33/static

次にアプリを作ります。今回はhogeという名前のアプリにします。

% cd ueblog33

% python manage.py startapp hoge

次にsettings.pyの設定。やはりhttp://code.google.com/appengine/articles/django.htmlの例でいいのですが

  • ROOT_URLCONF = 'urls' にする
  • INSTALLED_APPSに今さっきつくったアプリを入れる

ようにしましょう。

# Django settings for ueblog project.
import os
DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = (
    # ('Your Name', 'your_email@domain.com'),
)
MANAGERS = ADMINS

LANGUAGE_CODE = 'ja'

SITE_ID = 1

USE_I18N = True

MEDIA_ROOT = ''

MEDIA_URL = ''

ADMIN_MEDIA_PREFIX = '/media/'

SECRET_KEY = '' #ここは自分のやつを使ってください
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.load_template_source',
    'django.template.loaders.app_directories.load_template_source',
#     'django.template.loaders.eggs.load_template_source',
)

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    #'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.middleware.doc.XViewMiddleware',
)

ROOT_URLCONF = 'urls'

ROOT_PATH = os.path.dirname(__file__)
TEMPLATE_DIRS = (
    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
    # Always use forward slashes, even on Windows.
    # Don't forget to use absolute paths, not relative paths.
    ROOT_PATH + '/templates',
)

INSTALLED_APPS = (
    #'django.contrib.auth',
    'django.contrib.contenttypes',
    #'django.contrib.sessions',
    'django.contrib.sites',
    'hoge',
)

次にueblog33/urls.pyの編集

今回は

http://ueblog33.appspot.com/hoge/list/

で一行掲示板を作りたいので以下の通り

from django.conf.urls.defaults import *

urlpatterns = patterns('',
     (r'^hoge/list/$', 'hoge.views.bbs_list'),
)

次に/hoge/list/でアクセスされた場合の処理を任せるueblog/hoge/views.pyを作ります。

google app engineではdjango.modelを使えないのでgoogle.appenine.ext.dbを使ってます。また、googleのusersも使ってみました。

#!-*- coding:utf-8 -*-
from django.shortcuts import render_to_response
from django import newforms as forms
from google.appengine.ext import db
from google.appengine.api import users
import datetime

class Entry(db.Model):
    created = db.DateTimeProperty()
    body = db.StringProperty(required=True)

class EntryForm(forms.Form):
    body = forms.CharField(max_length=100, min_length=1)

def bbs_list(request):
    form = EntryForm(request.POST or None)
    if form.is_valid():
        e = Entry(body=unicode(request.POST["body"], 'utf8'),  #unicodeで渡す必要がある
            created=datetime.datetime.now())
        e.put()
        form = EntryForm()
    query = Entry.all()
    query.order('created')
    user = users.get_current_user()
    if user:
        nickname = ("%s (<a href='%s'>sign out</a>)" % (user.nickname(), users.create_logout_url("/hoge/list/")))
    else:
        nickname = "名無しさん <a href='%s'>Sign in or register</a>." %  users.create_login_url("/hoge/list/")
    return render_to_response('hoge/entry_list.html', {'form':form, 'object_list':query, 'nickname':nickname})

最後の行でhoge/entry_list.htmlというテンプレートを使うようにしてるのでそれもつくります。

% mkdir hoge/templates

% mkdir hoge/templates/hoge

まず、ベースになるbase.htmlを ueblog33/hoge/template/base.html

に作ります。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>{% block title %}{% endblock %}</title>
    </head>
    <body>
       {% block content %}
       {% endblock %}
    </body>
</html>

つぎに ueblog33/hoge/template/hoge/entry_list.htmlを作ります。

{% extends "base.html" %}
{% block title %}HOGE{% endblock %}
{% block content %}
<ul>
{% for object in object_list %}
<li>{{ object.body|escape }} : {{ object.created|date:"Y-m-d (D) H:i:s" }} : {{ nickname }}</li>
{% endfor %}
</ul>
<p>
  <form method="POST" action="." >
      {{ form }}
   <input type="submit" />
   </form>
</p>
 {% endblock %}

次にローカル環境で動かす

最初のueblog33の親ディレクトリに移動して

% cd ..

% python /usr/local/bin/dev_appserver.py ueblog33

をするとhttp://localhost:8080/hoge/list/

で先ほどつくったアプリが動く。Ctrl + C で止まる(Windowsは止まらないのでタスクマネージャーから無理矢理止めた)

ちなみにローカルのdatastoreをクリアするには

% python dev_appserver.py --clear_datastore ueblog33/

とやる。

うまく動いているようなら

% python /usr/local/bin/appcfg.py update ueblog33

でデプロイできます。

django.modelが使えないのならdjango使う旨味はない気もしましたが、djangoはテンプレートとかよくできてるのでそれはそれでありな気がします。

試してから書きますけどThe Expando Classはかなり衝撃的な気がします。

http://code.google.com/appengine/docs/datastore/expandoclass.html

※ よくよく考えてみたらgoogle app engineに入ってるdjango 0.96じゃなくて最新版のでもいいのではないかと思ったらeveresさんがやっていた。

http://www.everes.net/2008/apr/09/django-on-google-app-engine/

categories: googleappengine, django
Read and Post Comments

Next Page »
track feed ueblog