少し間が空いてしまいましたが、前回の続きをやっていきます。
ちなみに自分が作ったのはこれ。まだちょっとずつ改良してます。
http://my-1st-django-blog.herokuapp.com/
ハマりを解消した後、
heroku ps:scale web=1
から
heroku open
で”It worked!”ページを表示できるようになった。今回はここから簡単なブログページを作ってみる。
作り始める前に
まずは仮想環境virtualenvに入る。
source venv/bin/activate
herokuにログイン。
heroku login
これでOK。
アプリケーションを作成
プロジェクトディレクトリに移動し、アプリケーションを新規に作成。
python ../manage.py startapp blog_in_heroku
ちなみにこの段階でのディレクトリ構造はこんな感じ(_init_.pyや.pyc系は割愛)。
herokuDir(Procfile,venv,projectDir,manage.py,requirements.txt)
/projectDir(settings.py,urls.py,wsgi.py)
/appDir(admin.py,views.py,models.py,tests.py)
プロジェクトディレクトリ内にアプリケーションを作成したのは、単にそのほうが好みだったため。
続いて、作ったアプリケーションをINSTALLED_APPSに追加。
settings.py
INSTALLED_APPS = (
#…
'myproject.blog_in_heroku',
)
ついでにデータベースも設定する。herokuではpostgreSQLを使うのが簡単らしいので、遠慮なく活用させていただいた。
DATABASES部分を以下のように編集。
import dj_database_url
DATABASES = {
'default': {
'ENGINE':u'django.db.backends.sqlite3',
'NAME’:’forlocal.sqlite',
},
}
DATABASES['default'] = dj_database_url.config() # ローカルでのテスト時はコメントアウト
参考:http://momoto.github.io/blog/2013/08/16/deploying-django-1-dot-5-2-on-heroku/
一度sqlite3を指定しているのは、ローカル環境でのテスト用(その場合は.gitignoreにforlocal.sqliteを追加)。
もちろん直接書き込んでも構わないが(というより本当はそのほうが良いと思う)。
作ったアプリケーションを設計
クラスの形式でmodels.pyでデータベースに保存する内容を設定。
models.py
#..
class Entry(models.Model):
title = models.CharField(max_length=150)
body = models.TextField()
create_date = models.DateTimeField(auto_now_add=True)
update_date = models.DateTimeField(auto_now=True)
DJangoではこういったモデルからどんなデータベースが作られるのか知りたい場合、
python manage.py sql [appname]
このコマンドで自動生成されるSQL文を確認できる。
ただし、今回はデータベースがheroku上に作られるので、自分のPCでこれをそのままやっても確認することはできない。heroku上でコマンドを行いたいときは頭にheroku run
を付けて実行する。
つまりこんな感じ。
heroku run python manage.py sql [appname]
他にも色々とできるので覚えておくと非常に便利。
大丈夫そうだったら同じ要領でheroku run python manage.py syncdb
。データベースを作成する。
問題なくできたので先にURLconfを設定してしまう。
urls.py
#..
urlpatterns = patterns('myproject.blog_in_heroku.views',
url( r'^$','top_page',name='Blog' ),
url( r'^new/$','new',name='New' ),
# nameはページのタイトルと思いきやちょっと違う・・・?
今回はごく簡単にトップページと投稿ページだけ。
さらに管理者ページを使いたい場合はここらへんを見直す。
ビューは以下のような感じ。views.pyの他にforms.pyなるものを新しく作成。
forms.py
#coding:utf-8
from django import forms
class EntryForm(forms.Form):
title = forms.CharField(
label = u'題名',
)
body = forms.CharField(
label = u'本文',
widget = forms.Textarea(),
)
ここでは、チュートリアルでは出てこなかったdjangoのformsというモジュールを使っている。
CharFieldはおそらくデフォルトでは<input type=“text”>
を意味していると思われる。labelは対応したラベルのこと。
bodyでやっているように、形式をTextareaに指定することも可能。
これをviews.pyでインポートして使う。
views.py
from django.http import HttpResponse,HttpResponseRedirect
from django.template import loader,RequestContext
def top_page(req):
from myproject.blog_in_heroku.models import Entry
rows = Entry.objects.all().order_by('-create_date')
contexts = RequestContext(req,{
'rows':rows,
})
template = loader.get_template('blog_in_heroku/index.html')
return HttpResponse( template.render(contexts) )
def new(req):
from myproject.blog_in_heroku.forms import EntryForm
if req.method == 'POST':
form = EntryForm(req.POST)
else:
form = EntryForm()
if form.is_valid(): # 入力チェック
from myproject.blog_in_heroku.models import Entry
new_entry = Entry()
new_entry.title = form.cleaned_data['title']
new_entry.body = form.cleaned_data['body']
# form.cleaned_data['name']でformから個々に中身を取り出している
new_entry.save()
return HttpResponseRedirect('/')
# 投稿後はトップページにリダイレクト
contexts = RequestContext(req,{
'form':form,
})
template = loader.get_template('blog_in_heroku/new.html')
return HttpResponse( template.render(contexts) )
おおむねチュートリアルで学んだこととやっていることは変わらない。
new関数のほうでcleaned_dataという知らないメソッドが登場しているとはいえ、やっていることは流れでなんとなく想像できる。
ここまででページの定義付けは終わり。いよいよ実際の画面を作る。
画面づくり
view関数内で指定した二つのテンプレート、index.htmlとnew.htmlを書く。かっこつけてBootstrapも使ってみた。
自分はherokuDir/templates/blog_in_heroku/index.html
という風に作ったが、templates以下が同じならどこにつくっても問題はない(herokuDirの外となるとさすがに無理だが)。
index.html
<html>
<head>
<title>ぶろぐ</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap-theme.min.css">
<script src="https://code.jquery.com/jquery.js"></script>
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
{% for row in rows %}
<p>
<div>
<h2>{{ row.title }}</h2>
</div>
<div>投稿日{{ row.create_date|date:"Y/m/d" }}</div>
<div>{{ row.body }}</div>
</p>
{% endfor %}
<a href="/new/">
<button type="button" class="btn btn-primary">投稿する!</button>
</a>
</div>
</body>
</html>
new.html
<html>
<head>
<title>投稿</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap-theme.min.css">
<script src="https://code.jquery.com/jquery.js"></script>
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<form role="form" action="" method="post">
{% csrf_token %}
<div class="form-group">
<table><tbody>
{{form}}
<tr><td cols="2">
<input type="submit" class="btn btn-primary" value="投稿!" />
<a href="/">
<button type="button" class="btn btn-warning">取消</button>
</a>
</td></tr>
</tbody></table>
</div>
</form>
</div>
</body>
</html>
indexページではよくやるようにfor文でデータベースの中身を一つ一つ取り出している。「投稿日」の部分に{{ row.create_date|date:”Y/m/d” }}
という記述があるが、間の”|”(パイプ)はフィルタという機能でテンプレート関数に簡単な加工をする時に使うらしい。どこまでの加工ができるのかは今のところ不明。まあ、スクリプト側で加工してから渡せばいいのだからこれも一種のショートカットみたいなものだろう。
newページのフォーム部分は前に書いたforms.pyのEntryFormクラスで定義したものをviews.pyのform変数としてまとめて、ここで実体化させている感じ(そのせいでBootstrapのクラスを付けられず見た目がかっこ悪いのだが)。あと{% csrf_token %}。正直にいうとこれで具体的に何をしているのか分かっていないが、とりあえず書いた。
最後に、テンプレートの場所をDJangoに教えてあげる必要がある。
settings.py
TEMPLATE_DIRS = (
'/app/templates',
)
これまでのherokuDirに当たる部分は、heroku上ではappというディレクトリになっている。以下は自分の環境とまったく同じなので(たぶん)分かりやすい。ちなみにこのことはheroku run pwd
等で確認した。
herokuにアップ
あとは流れるように
git add .
git commit -m “lets test”
git push heroku master
そして起動。
heroku ps:scale web=1
これが成功したならもうページは公開されている。開いてみよう。
heroku open
すごい楽しい。これだけなら料金もかからなそうだし、しばらくこのまま取っておこう。