09

Django

いまどきのフレームワークのワークフローをしらべてみたくてpython + djangoをいじってみる。 開発環境はだいたいこんな感じ。

Yammerのようなスレッド型のチャットのような物を作ってみる。

PyCharmをインストールして、プロジェクトを生成するとPythonの環境を整えてくれるようだ。使いたい外部モジュールがあればそれをインストールする事もできるみたい。

余談だけど、PythonにしろRubyにしろNode.jsにしろ、当たり前に実行完了をインストールしてしまうと違うバージョンのランタイムを使いたいときに環境を切り替えるためのツール(virtualenv とか rbenvとかなんとか)いれる事になるので、あらかじめ入れておこうとか思うと「今、Ruby通の間での最新流行は・・・」とか気になり始めて本題のアプリ開発にたどり着くまでによけいな寄り道をする羽目になっちゃったりするんだけど、この辺面倒を見てくれると悩む余地がなくて楽なんだな。

進め方

基本的には公式のチュートリアルに従ってやってみる。 日本語のほうは若干バージョンが古いみたいだけど、あまり問題ないみたい。

django のプロジェクト

djangoのプロジェクトは複数のサイト(アプリケーション)をまとめた形の構成になっている。プロジェクトのフォルダの下に、アプリケーション毎フォルダを切っていくようだ。 プロジェクトと同じ名前のフォルダにはトップレベルの設定ファイルがおいてある。ここにアプリケーションコードをおく訳ではないみたい。

設定を書く

とりあえずデータベースの設定画必要になるのでsettings.pyを見てみる。 データベースの設定だけじゃなくいろいろ重要な設定が入っているので、全体眺めてみた方がいいだろう。

まずはモデル定義

DjangoはORMを持っているので、その作法に従ってモデルを定義。これはPythonのクラスとして定義するんだけど、django.models.Model クラスを継承した形で定義し、クラスのフィールドはあらかじめ用意されている*Fieldオブジェクトとして定義することで、テーブルとテーブルのカラムとしてマッピングされる。

from django.db import models
from django.contrib.auth.models import User


# Create your models here.
class Topic(models.Model):
    created_on = models.DateTimeField(auto_now_add=True)
    updated_on = models.DateTimeField(auto_now=True, auto_now_add=True)


class Comment(models.Model):
    topic = models.ForeignKey(Topic)
    writer = models.ForeignKey(User)
    comment = models.TextField(max_length=4000)
    created_on = models.DateTimeField(auto_now_add=True)
    updated_on = models.DateTimeField(auto_now=True)

クラスを定義し終わったら、manage.py syncdb コマンドを実行する事でクラスの情報からデータベースへ反映してくれる。PyCharmなら option + r で manage.pyを実行するためのテキストボックスが開くので、そこから実行してやれば良い。

新しくモデルを追加したり削除したりしたばあいも基本 manage.py で良い見たいなんだけど、どこかのFAQで開発チームは基本的にデータベースのマイグレーションは自動で行うべきではないと考えているみたいなことを書いていた。manage.py にはテーブルの内容をダンプしたりするコマンドもあるようなので、場合によってはそういったコマンドを駆使して自分でデータベースのデータをマイグレーションする必要があるケースも出てくるんだろう。

ビューを作る

ビューは基本的に関数として定義するだけなんだけど、テンプレートエンジンがあるのでテンプレートのコンテキストを作成したらテンプレートのパスを指定してコンテキストをテンプレートエンジンに渡す感じ。結果はそのまま戻ってくるので、ビューの関数の戻り値として返せば良いようだ。

テンプレートは普通のHTMLで書く。 本来野コードではクライアントサイドでいろいろやっているのでjavascriptを長々書いているのだけど、 その辺はあまり重要でないので、以下はbodyタグだけ抜き出している。

    <body>
        <div class="new-topic">
            <form class="comment" action="/topics/add" method="POST">
                {% csrf_token %}
                <input type="text" name="comment" size="120" />
                <input type="button" class="add-comment" name="command" value="Send" />
            </form>
        </div>
        <div>
            {% for topic in topics %}
            <ul class="topic" data-id="{{ topic.id }}">
                {% for comment in topic.comment_set.all %}
                <li class="comment" data-id="{{ comment.id }}">
                    <div class="comment">{{ comment.comment }}</div>
                    <div class="username">by {{ comment.writer.username }}</div>
                </li>
                {% endfor %}
                <li class="comment-form" >
                    <form class="comment" action="/topic/{{ topic.id }}/comments/add" method="POST">
                        {% csrf_token %}
                        <input type="text" name="comment" size="120" />
                        <input type="button" class="add-comment" name="command" value="Send" />
                    </form>
                </li>
            </ul>
            {% endfor %}
        </div>
    </body>

csrf_token タグはCSRF対策らしいのだが、具体的に何をしているのかについてはよく理解していない。サーバサイドでのリクエストオブジェクトのハンドリングとセットになっていて、ミドルウェアがよろしく検証してくれるのだろう。 ほかはまあよくあるテンプレート言語がブレースで埋め込まれているだけという感じ。

このテンプレートを templates/topics.html という名前で保存した上で、ビューメソッドは以下のように書く。

def comment_list(request):
    topics = Topic.objects.order_by('-updated_on').all()
    return render_to_response('topics.html', RequestContext(request, {'topics': topics}))

RequestContext が上記でCSRFのサーバサイド側でのハンドリングと述べている点で、おそらくトークンの生成などをするのだろう。基本的にテンプレートに値を埋め込むだけなら RequestContextオブジェクトではなく、単に辞書オブジェクト(上記では {'topics': topics} )を render_to_response 渡してやればよい。

ルーティングを追加

ビューをつくってもそれに対してURLを与えないと呼び出す事ができない。 これはプロジェクトフォルダ配下にあるurls.pyに書かれている。 最初からサンプル的な物を書かれているので、それに習って追加してやれば良い。

ここまでで

モデルを定義してビューをつくってルーティングを用意してやればそれでDBと連携した画面は表示できるようになる。

多分、あまりあれこれ疑ったりしなければこの辺は素直に実装できる。