この記事は、LISPっぽいけど一応Python その2 Advent Calendar 2015の5日目の記事です。
HyのREADMEに"Django + LISP"という一文があり、DjangoアプリをHyでも実装できるというコンセプトを示したソースコードが示されていますが、これだけでは何をどうすればいいか分からないので、startproject
するところからステップを追って解説します。
githubにレポジトリを作り、ステップごとにコミットを分けてpushしたので参考にしてください。
startproject
する
ひとまず普通のPythonでdjango-admin.py startproject
します。使用しているDjangoのバージョンは1.9です。
django-admin.py startproject hellohy
startproject
した段階のコミットがこれです。
*.py
を*.hy
に書き換える
すべてのプログラミング言語はLISPに至るので、出来る限りPythonコードを排除して、Hyで置き換えたいと思います。
プロジェクトの作成直後のコードをHyに書き換えたコミットがこれです。
少し躓いた点は以下の2つです。
-
manage.py
をHyに置き換えても、hy manage.hy runserver
が動かなかったので、このファイルだけは諦めました。 - Hyでモジュールを定義するには、起動スクリプト(この場合
manage.py
)にimport hy
を入れる必要があるようです。 - シングルクォートはHy(LISP)では特殊な意味を持つので、文字列はすべてダブルクォートに変更しました。
Pythonのimport x
, from y import z
のHyの変換の仕方は、ここを見れば一目瞭然です。
この段階で、manage.py
以外のすべてのpy
ファイルを消しても、python manage.py runserver
が動くようになり、実際にブラウザでもアクセスできます。
app_template
を作成
startproject
の後はstartapp
でDjangoアプリケーション・モジュールを追加するのがDjangoの定番の流れですが、この段階でstartapp
してもPythonモジュールが追加されるのは分かりきっていたので、先にアプリケーション・テンプレートを追加します。
それが、このコミットです。
このapp_template
を使ってstartapp
を実行します。
python manage.py startapp --template=hellohy/app_template -ehy myapp
ポイントは、
-
--template
で独自のアプリケーション・テンプレートのディレクトリを指定する。 -
-e
でテンプレートとして扱うファイルの拡張子を追加する。
ことです。
上記コマンドでmyapp
というアプリケーションを作り、myapp.views.top
というごく単純なテンプレートを表示するビューを追加したコミットがこちらです。
モデルを定義する
HyのチュートリアルにDjangoのモデルっぽいサンプルコードがありますが、この段階でそれが擬似コードではなく、実際に動くコードであるというのが確認できます。
モデルを足して、データベース上のデータを表示するようにしたコミットがこちらです。
モデルの定義は以下のようになっています。
(import [django.db [models]]
[django.utils.timezone :as timezone])
(defclass Topic [models.Model]
[title (models.TextField)
url (models.URLField)
created_at (models.DateTimeField :default timezone.now)])
Topic
とは何らかのニュースのタイトルをソースのURLを表したオブジェクトと考えてください。重要なのは次の点です。
-
defclass
は最新開発版のHyの構文を使っています。 - クラスプロパティは
defclass
マクロの3番目の引数にリストで与えます - Pythonのキーワード引数
models.DateTimeField(default=timezone.now)
は(models.DateTimeField :default timezone.now)
のように表現します。
モデルを定義した後に、makemigrations
, migrate
する手順は通常のDjangoと同じです。
ビューを実装する
上で定義したTopic
オブジェクトを複数件取得してテンプレートをレンダリングする例です。
(import [django.shortcuts [render]]
[myapp.models [Topic]])
(defn top [req]
(def topics (-> (Topic.objects.all)
(.order_by "-id")))
(render req "top.html" {"topics" topics}))
プライマリキーでオブジェクトを取得してテンプレートをレンダリングする例。
(import [django.shortcuts [render]]
[django.http [Http404]]
[myapp.models [Topic]])
(defn topic_detial [req topic_id]
(def topic (try
(Topic.objects.get :pk topic_id)
(except [e Topic.DoesNotExist] (raise Http404))))
(render req "topics/topic_detail.html" {"topic" topic}))
以下のコミットで一覧と詳細のビューを追加するところまで実装しています。
ユニットテスト
Djangoを使うことのメリットにユニットテストがしやすいということがあると思うので、今回もユニットテストを足しておくことにしましょう。
それが、このコミットです。
本当はこのtests.hy
だけでユニットテストが実行できればよかったのですが、テストランナーが.hy
を見つけてくれなかったので、裏技のような感じになってしまいますが、同名の空の.py
ファイルを足しています。
この裏技は何とかしたいと思いますが、とりあえずテストは通っています。
$ python manage.py test myapp
Creating test database for alias 'default'...
....
----------------------------------------------------------------------
Ran 4 tests in 0.051s
OK
Destroying test database for alias 'default'...
最後に
HyでDjangoアプリを実装していく手順を紹介しました。
ご自身でも試してもらえればお分かりになると思いますが、HyでもDjangoのパワーを100%引き出せそうなので、すべてのプログラミング言語はLISPに至るというのを実感できますね。
次回はHyで書いたDjangoアプリを実環境にデプロイする方法を通じて、Hyの可能性を探っていきたいと思います。