| 1 | ======================================================= |
|---|
| 2 | Django Hands on for beginners. |
|---|
| 3 | ======================================================= |
|---|
| 4 | |
|---|
| 5 | 超基本 |
|---|
| 6 | ------------------------------------------------------------------------- |
|---|
| 7 | * Webの仕組み |
|---|
| 8 | |
|---|
| 9 | * Modelって何? |
|---|
| 10 | |
|---|
| 11 | |
|---|
| 12 | → 拙著 `Django×Python`_ 等の入門書を読んでください。 |
|---|
| 13 | |
|---|
| 14 | |
|---|
| 15 | .. _`Django×Python`: http://www.amazon.co.jp/dp/477413760X |
|---|
| 16 | |
|---|
| 17 | |
|---|
| 18 | Djangoの超基本(BASE) |
|---|
| 19 | ------------------------------------------------------------------------- |
|---|
| 20 | マルチユーザblogっぽいものを作ろう! |
|---|
| 21 | |
|---|
| 22 | ブログのモデルを考える |
|---|
| 23 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
|---|
| 24 | * エントリー → タイトル、本文、タグ、公開日、書いた人 |
|---|
| 25 | * タグ → タグ名、持ち主 |
|---|
| 26 | |
|---|
| 27 | |
|---|
| 28 | エントリーのモデルサンプル: ForeignKeyやManyToManyに指定するモデルは、 |
|---|
| 29 | 指定よりも先に定義が読み込まれていなければならない。 |
|---|
| 30 | どうしようもない場合には文字列で定義すればよい。 |
|---|
| 31 | :: |
|---|
| 32 | |
|---|
| 33 | |
|---|
| 34 | class Tag(models.Model): |
|---|
| 35 | name = models.CharField(u'タグ名', max_length=50) |
|---|
| 36 | user = models.ForeignKey(User) |
|---|
| 37 | |
|---|
| 38 | class Meta: |
|---|
| 39 | ordering = ['user', 'name'] |
|---|
| 40 | |
|---|
| 41 | class Entry(models.Model): |
|---|
| 42 | title = models.CharField(u'タイトル', max_length=50) |
|---|
| 43 | body = models.TextField(u'本文') |
|---|
| 44 | tags = models.ManyToManyField(Tag, blank=True) |
|---|
| 45 | pub_date = models.DateTimeField(default=datetime.now) |
|---|
| 46 | author = models.ForeignKey(User) |
|---|
| 47 | |
|---|
| 48 | class Meta: |
|---|
| 49 | ordering = ['pub_date'] |
|---|
| 50 | |
|---|
| 51 | |
|---|
| 52 | アプリケーション構成を考える |
|---|
| 53 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
|---|
| 54 | タグを別にしてもよいので、せっかくだから別にする。 |
|---|
| 55 | |
|---|
| 56 | |
|---|
| 57 | * Entry → ブログのメインモデル |
|---|
| 58 | * Tag → ブログに限らず使える |
|---|
| 59 | |
|---|
| 60 | |
|---|
| 61 | * 必要なPYTHONPATHを通す |
|---|
| 62 | |
|---|
| 63 | |
|---|
| 64 | システムのPYTHONPATHか、自分のPYTHONPATHを通す。 |
|---|
| 65 | |
|---|
| 66 | Windows |
|---|
| 67 | :: |
|---|
| 68 | |
|---|
| 69 | > set PYTHONPATH=%PYTHONPATH%;C:¥¥hackathon_handson |
|---|
| 70 | |
|---|
| 71 | bash |
|---|
| 72 | :: |
|---|
| 73 | |
|---|
| 74 | $ export PYTHONPATH=$PYTHONPATH:/home/hoge/hackathon_handson |
|---|
| 75 | |
|---|
| 76 | |
|---|
| 77 | * プロジェクトを作る |
|---|
| 78 | |
|---|
| 79 | |
|---|
| 80 | :: |
|---|
| 81 | |
|---|
| 82 | django-admin.py startproject hackathon |
|---|
| 83 | |
|---|
| 84 | |
|---|
| 85 | * アプリケーションを二つ作る(PYTHONPATHを通したディレクトリ直下に作る) |
|---|
| 86 | |
|---|
| 87 | |
|---|
| 88 | プロジェクトの直下ではなく、他の場所に作ってみる。名前は他とかぶらないものにした方が良い。とりあえず駄目な名前を付ける。 |
|---|
| 89 | :: |
|---|
| 90 | |
|---|
| 91 | django-admin.py startapp blog |
|---|
| 92 | django-admin.py startapp tags |
|---|
| 93 | |
|---|
| 94 | |
|---|
| 95 | * settings.pyに最低限の設定をする |
|---|
| 96 | |
|---|
| 97 | |
|---|
| 98 | データベースの設定 |
|---|
| 99 | :: |
|---|
| 100 | |
|---|
| 101 | import os |
|---|
| 102 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) |
|---|
| 103 | DATABASE_ENGINE = 'sqlite3' |
|---|
| 104 | DATABASE_NAME = os.path.join(BASE_DIR, 'data.sqlite3') |
|---|
| 105 | |
|---|
| 106 | |
|---|
| 107 | アプリケーションの設定 |
|---|
| 108 | :: |
|---|
| 109 | |
|---|
| 110 | INSTALLED_APPS = ( |
|---|
| 111 | #元から書いてあるもの |
|---|
| 112 | 'blog', |
|---|
| 113 | 'tags', |
|---|
| 114 | ) |
|---|
| 115 | |
|---|
| 116 | タイムゾーンを日本にする |
|---|
| 117 | :: |
|---|
| 118 | |
|---|
| 119 | TIME_ZONE = 'Japan' |
|---|
| 120 | |
|---|
| 121 | |
|---|
| 122 | * テーブルを作る |
|---|
| 123 | |
|---|
| 124 | |
|---|
| 125 | syncdbする。スーパーユーザは作っておく |
|---|
| 126 | :: |
|---|
| 127 | |
|---|
| 128 | $ chmod 755 ./manage.py |
|---|
| 129 | $ ./manage.py syncdb |
|---|
| 130 | |
|---|
| 131 | |
|---|
| 132 | |
|---|
| 133 | モデルの扱いに慣れる |
|---|
| 134 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
|---|
| 135 | |
|---|
| 136 | * tags/models.pyにTagクラスを、blog/models.pyにEntryを記述する。 |
|---|
| 137 | |
|---|
| 138 | * ASCII以外の文字をスクリプトファイルに書く場合にはエンコーディングの指定を忘れないこと。ファイルのエンコーディングはutf-8にする。 |
|---|
| 139 | :: |
|---|
| 140 | |
|---|
| 141 | # encoding:utf-8 |
|---|
| 142 | |
|---|
| 143 | * ForeignKeyで指定しているモデルのインポートを忘れずにする。Entryモデルは、公開日のデフォルトとして現在日時を使うのでdatetime.datetimeのインポートも忘れずに。 |
|---|
| 144 | :: |
|---|
| 145 | |
|---|
| 146 | #tags/models.py |
|---|
| 147 | from django.contrib.auth.models import User |
|---|
| 148 | |
|---|
| 149 | #blog/models.py |
|---|
| 150 | from django.contrib.auth.models import User |
|---|
| 151 | from tags.models import Tag |
|---|
| 152 | from datetime import datetime |
|---|
| 153 | |
|---|
| 154 | * プロジェクトの設定を持ったままインタラクティブシェルを起動する。 |
|---|
| 155 | :: |
|---|
| 156 | |
|---|
| 157 | $ ./manage.py shell |
|---|
| 158 | >>> |
|---|
| 159 | |
|---|
| 160 | * データ操作するためのモデルをインポートする。 |
|---|
| 161 | :: |
|---|
| 162 | |
|---|
| 163 | >>> from blog.models import Entry |
|---|
| 164 | >>> from tags.models import Tag |
|---|
| 165 | >>> from django.contrib.auth.models import User |
|---|
| 166 | |
|---|
| 167 | * スーパーユーザを取得しておく |
|---|
| 168 | :: |
|---|
| 169 | |
|---|
| 170 | >>> spruser = User.objects.all()[0] |
|---|
| 171 | |
|---|
| 172 | * エントリを登録してみる。 |
|---|
| 173 | :: |
|---|
| 174 | |
|---|
| 175 | >>> from datetime import datetime |
|---|
| 176 | >>> ent = Entry(title=u'Test Entry', body=u'Entry Body. Test, Test, Test', pub_date=datetime.now()) |
|---|
| 177 | >>> ent.author = spruser |
|---|
| 178 | >>> ent.save() |
|---|
| 179 | |
|---|
| 180 | * データベースに保存されたエントリを取り出してみる |
|---|
| 181 | :: |
|---|
| 182 | |
|---|
| 183 | >>> db_ent = Entry.objects.get(pk=1) |
|---|
| 184 | >>> print db_ent.title |
|---|
| 185 | Test Entry |
|---|
| 186 | |
|---|
| 187 | * タグを登録してみる。ForeignKey(author)に値を設定する方法 |
|---|
| 188 | :: |
|---|
| 189 | |
|---|
| 190 | #fkを表すフィールドにUserインスタンスを設定 |
|---|
| 191 | >>> tg1 = Tag(name=u'Test Tag1', user=spruser) |
|---|
| 192 | >>> tg1.save() |
|---|
| 193 | |
|---|
| 194 | #fk_idにUserインスタンスのpkを設定 |
|---|
| 195 | >>> tg2 = Tag(name=u'Test Tag2', user_id=spruser.id) |
|---|
| 196 | >>> tg2.save() |
|---|
| 197 | |
|---|
| 198 | #インスタンスのfkを表すフィールドにUserインスタンスを設定 |
|---|
| 199 | >>> tg3 = Tag(name=u'Test Tag3') |
|---|
| 200 | >>> tg3.user = spruser |
|---|
| 201 | >>> tg3.save() |
|---|
| 202 | |
|---|
| 203 | * エントリにタグを設定してみる。 |
|---|
| 204 | :: |
|---|
| 205 | |
|---|
| 206 | >>> ent.tags.add(tg1) |
|---|
| 207 | >>> ent.tags.all() |
|---|
| 208 | [<Tag: Tag object>] |
|---|
| 209 | |
|---|
| 210 | * エントリからタグを取り除く |
|---|
| 211 | :: |
|---|
| 212 | |
|---|
| 213 | >>> ent.tags = [] |
|---|
| 214 | >>> ent.tags.all() |
|---|
| 215 | [] |
|---|
| 216 | |
|---|
| 217 | * エントリに複数のタグを一度に設定する |
|---|
| 218 | :: |
|---|
| 219 | |
|---|
| 220 | >>> ent.tags.add(tg1, tg2) |
|---|
| 221 | >>> ent.tags.all() |
|---|
| 222 | [<Tag: Tag object>, <Tag: Tag object>] |
|---|
| 223 | |
|---|
| 224 | * エントリに複数のタグを一度に設定する(代入) |
|---|
| 225 | :: |
|---|
| 226 | |
|---|
| 227 | >>> ent.tags.clear() #これでもタグの設定を消せる |
|---|
| 228 | >>> ent.tags.all() |
|---|
| 229 | >>> ent.tags = [tg1,tg2] |
|---|
| 230 | >>> ent.tags.all() |
|---|
| 231 | |
|---|
| 232 | 遅延評価とキャッシュを理解する |
|---|
| 233 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
|---|
| 234 | |
|---|
| 235 | * SQLをデバッグアウトするために、下準備をする |
|---|
| 236 | |
|---|
| 237 | * eggでインストールしていなければ、django.db.backends.utilのCursorDebugWrapperのexecuteでsqlをprintする。 |
|---|
| 238 | * eggでインストールしている場合には、django.db.connection.queriesを都度デバッグアウトする。 |
|---|
| 239 | |
|---|
| 240 | * 一度参照したFKはキャッシュされる |
|---|
| 241 | :: |
|---|
| 242 | |
|---|
| 243 | >>> db_ent.author |
|---|
| 244 | #SQL |
|---|
| 245 | <User: hoge> |
|---|
| 246 | >>> db_ent.author |
|---|
| 247 | <User: hoge> |
|---|
| 248 | |
|---|
| 249 | * managerを使ってQuerysetを生成・評価すると毎回SQLが発行される。毎回新しいQuerysetが生成されるため。 |
|---|
| 250 | :: |
|---|
| 251 | |
|---|
| 252 | >>> db_ent.tags.all() |
|---|
| 253 | #SQL |
|---|
| 254 | [<Tag: Tag object>, <Tag: Tag object>] |
|---|
| 255 | >>> db_ent.tags.all() # |
|---|
| 256 | #SQL |
|---|
| 257 | [<Tag: Tag object>, <Tag: Tag object>] |
|---|
| 258 | |
|---|
| 259 | * Querysetは評価されたタイミングでSQLが発行され、一度評価されたQuerysetは再度評価されない |
|---|
| 260 | :: |
|---|
| 261 | |
|---|
| 262 | >>> query = db_ent.tags.all() |
|---|
| 263 | >>> query #Querysetを評価した時点でSQLが発行される |
|---|
| 264 | #SQL |
|---|
| 265 | [<Tag: Tag object>, <Tag: Tag object>] |
|---|
| 266 | >>> query #一度評価したQuerysetは再度評価されない |
|---|
| 267 | [<Tag: Tag object>, <Tag: Tag object>] |
|---|
| 268 | |
|---|
| 269 | * 一度評価されたQuerysetも結果がキャッシュされているだけで、通常のQuerysetのまま |
|---|
| 270 | :: |
|---|
| 271 | |
|---|
| 272 | #一度評価したQuerysetのfilterを呼ぶと、きちんと新しいQuerysetが生成される。 |
|---|
| 273 | >>> x = query.filter(pk=1) |
|---|
| 274 | #つまり、評価すると正しい条件のついたSQLを発行する |
|---|
| 275 | >>> x |
|---|
| 276 | #SQL |
|---|
| 277 | [<Tag: Tag object>] |
|---|
| 278 | #一度評価したQuerysetのfilterを呼び出す。 |
|---|
| 279 | #再度Queryset評価しても、何もなかったかのように振る舞う。 |
|---|
| 280 | >>> query |
|---|
| 281 | [<Tag: Tag object>, <Tag: Tag object>] |
|---|
| 282 | |
|---|
| 283 | Djangoの必修(BASE) |
|---|
| 284 | ------------------------------------------------------------------------- |
|---|
| 285 | |
|---|
| 286 | Adminを使う |
|---|
| 287 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
|---|
| 288 | |
|---|
| 289 | * URLの設定 |
|---|
| 290 | |
|---|
| 291 | Adminを使うため、hackathon/urls.pyのコメントアウトされている部分を外す。 |
|---|
| 292 | :: |
|---|
| 293 | |
|---|
| 294 | # Uncomment the next two lines to enable the admin: |
|---|
| 295 | from django.contrib import admin |
|---|
| 296 | admin.autodiscover() |
|---|
| 297 | .... |
|---|
| 298 | (r'^admin/(.*)', admin.site.root), |
|---|
| 299 | |
|---|
| 300 | * Adminをインストールする |
|---|
| 301 | |
|---|
| 302 | hackathon/settings.pyのINSTALLED_APPSに次の行を追加 |
|---|
| 303 | :: |
|---|
| 304 | |
|---|
| 305 | 'django.contrib.admin', |
|---|
| 306 | |
|---|
| 307 | データベースにテーブルを作る |
|---|
| 308 | :: |
|---|
| 309 | |
|---|
| 310 | ./manage.py syncdb |
|---|
| 311 | |
|---|
| 312 | * 開発サーバを起動する |
|---|
| 313 | :: |
|---|
| 314 | |
|---|
| 315 | ./manage.py runserver |
|---|
| 316 | |
|---|
| 317 | 起動したら、http://localhost:8000/admin/をブラウザで開いてログインする。 |
|---|
| 318 | |
|---|
| 319 | * Adminでまずすること |
|---|
| 320 | |
|---|
| 321 | * Sitesの設定を変更する |
|---|
| 322 | |
|---|
| 323 | ドメイン名にlocalhost:8000、表示名にdevelopmentと入力して保存。 |
|---|
| 324 | |
|---|
| 325 | * グループを作成する |
|---|
| 326 | |
|---|
| 327 | Bloggerという名前で、blogとtagに権限を付与する。権限リストは動的に絞り込める。 |
|---|
| 328 | |
|---|
| 329 | .. image:: images/create_group.png |
|---|
| 330 | |
|---|
| 331 | * ユーザを作成する |
|---|
| 332 | |
|---|
| 333 | 今後のために2つ作成する。スタッフ権限にチェックを入れて、Bloggerグループに所属させる。 |
|---|
| 334 | |
|---|
| 335 | .. image:: images/user_list.png |
|---|
| 336 | |
|---|
| 337 | * EntryとTagをAdminに表示されるようにする。 |
|---|
| 338 | |
|---|
| 339 | * admin.pyの追加 |
|---|
| 340 | |
|---|
| 341 | tagsとblogにadmin.pyというファイルを作成して、Adminで使うモデルとしてAdminFormを登録する |
|---|
| 342 | :: |
|---|
| 343 | |
|---|
| 344 | #tags/admin.py |
|---|
| 345 | from django.contrib import admin |
|---|
| 346 | from tags.models import Tag |
|---|
| 347 | |
|---|
| 348 | admin.site.register(Tag, admin.ModelAdmin) |
|---|
| 349 | |
|---|
| 350 | #blog/admin.py |
|---|
| 351 | from django.contrib import admin |
|---|
| 352 | from blog.models import Entry |
|---|
| 353 | |
|---|
| 354 | admin.site.register(Entry, admin.ModelAdmin) |
|---|
| 355 | |
|---|
| 356 | スーパーユーザでログインしているので、一度ログアウトしてBloggerグループのユーザでログインし直す。 |
|---|
| 357 | |
|---|
| 358 | .. image:: images/blogger_admin_page.png |
|---|
| 359 | |
|---|
| 360 | * モデルのデータ一覧で表示される項目を変更する |
|---|
| 361 | |
|---|
| 362 | Entryモデルの一覧を表示してみると、悲しい一覧が見えるはず。 |
|---|
| 363 | |
|---|
| 364 | .. image:: images/sad_entry_list.png |
|---|
| 365 | |
|---|
| 366 | * admin.ModelAdminを継承したカスタムフォームを作成する |
|---|
| 367 | :: |
|---|
| 368 | |
|---|
| 369 | #blog/admin.py |
|---|
| 370 | class EntryModelAdmin(admin.ModelAdmin): |
|---|
| 371 | list_display = ['title', 'author', 'pub_date'] |
|---|
| 372 | |
|---|
| 373 | admin.site.register(Entry, EntryModelAdmin) |
|---|
| 374 | |
|---|
| 375 | 幸せな一覧になった? |
|---|
| 376 | |
|---|
| 377 | list_displayにフィールド名をリストで設定すると一覧の表示をカスタマイズできます。 |
|---|
| 378 | |
|---|
| 379 | .. image:: images/happy_entry_list.png |
|---|
| 380 | |
|---|
| 381 | * オブジェクトの自己表現を変更する |
|---|
| 382 | |
|---|
| 383 | **しかし!** 編集画面を表示すると、ManyToManyが酷いことになっています。 |
|---|
| 384 | |
|---|
| 385 | .. image:: images/sad_object_repr.png |
|---|
| 386 | |
|---|
| 387 | 関連の値は、関連のインスタンスを文字列表現にしたものを表示しています。 |
|---|
| 388 | |
|---|
| 389 | 文字列表現を操るため、Tagモデルに **__unicode__** メソッドを追加します。 |
|---|
| 390 | :: |
|---|
| 391 | |
|---|
| 392 | #tags/models.pyのTagクラスに以下を追加 |
|---|
| 393 | def __unicode__(self): |
|---|
| 394 | return u'%s(%s)' % (self.name, self.user) |
|---|
| 395 | |
|---|
| 396 | ちょっと幸せになった? |
|---|
| 397 | |
|---|
| 398 | .. image:: images/happy_object_repr.png |
|---|
| 399 | |
|---|
| 400 | ついでに、Tagの一覧を表示してみると、同様の表記に変わっているはずです。 |
|---|
| 401 | |
|---|
| 402 | * 編集画面のフィールド並び順を変更する |
|---|
| 403 | |
|---|
| 404 | 最近のネットブックは画面の縦方向が異常に短いですよね。 |
|---|
| 405 | |
|---|
| 406 | 編集画面はモデルのフィールドが順番に縦に並んでいるだけなので、少し並べかえてみましょう。 |
|---|
| 407 | また、公開日はデフォルトのママにする可能性が高いので、普段は目に入らないようにしておきましょう。 |
|---|
| 408 | |
|---|
| 409 | EntryModelAdminのlist_displayに続いてfieldsetsを追加します |
|---|
| 410 | :: |
|---|
| 411 | |
|---|
| 412 | #blog/admin.py |
|---|
| 413 | fieldsets = ( |
|---|
| 414 | ('Core Information', { |
|---|
| 415 | 'fields': (('title', 'author'), 'body',) |
|---|
| 416 | }), |
|---|
| 417 | ('Options', { |
|---|
| 418 | 'classes': ['collapse',], |
|---|
| 419 | 'fields': (('pub_date', 'tags'), ) |
|---|
| 420 | }) |
|---|
| 421 | ) |
|---|
| 422 | |
|---|
| 423 | fieldsetsはフィールドの表示を制御します。 |
|---|
| 424 | |
|---|
| 425 | とりあえずすっきりしました。 |
|---|
| 426 | |
|---|
| 427 | .. image:: images/admin_fieldsets.png |
|---|
| 428 | |
|---|
| 429 | オプションは、クリックすると開きます。 |
|---|
| 430 | |
|---|
| 431 | .. image:: images/admin_fieldsets_open.png |
|---|
| 432 | |
|---|
| 433 | * 一覧画面をごっちゃりする |
|---|
| 434 | |
|---|
| 435 | 一覧といえば絞り込みですよね。 |
|---|
| 436 | |
|---|
| 437 | .. image:: images/admin_more.png |
|---|
| 438 | |
|---|
| 439 | * list_filter |
|---|
| 440 | |
|---|
| 441 | list_filterにフィールド名をリストで設定すると一覧の右に絞り込み機能がつきます。 |
|---|
| 442 | |
|---|
| 443 | 指定できるフィールドはBooleanField、ForeignKeyやManyToManyFieldです。 |
|---|
| 444 | |
|---|
| 445 | * date_hierarchy |
|---|
| 446 | |
|---|
| 447 | date_hierarchyに日付型のフィールド名を設定すると、リスト上部に日付での絞り込み機能がつきます。 |
|---|
| 448 | |
|---|
| 449 | 絞り込みは、年→年月→年月日で順に絞れます。また、絞り込みの候補にはデータの存在する対象しか出ません。 |
|---|
| 450 | |
|---|
| 451 | * search_fields |
|---|
| 452 | |
|---|
| 453 | search_fieldsにフィールと名をリストで設定すると文字列検索機能がつきます。 |
|---|
| 454 | |
|---|
| 455 | * ManyToManyFieldの表示をUserの権限リストのようにしてみる |
|---|
| 456 | |
|---|
| 457 | 候補の数が増えてくるとどれを選択したのかわからなくなってきてしまいます。 |
|---|
| 458 | |
|---|
| 459 | そもそも選択自体に困難が伴うこともあります。 |
|---|
| 460 | :: |
|---|
| 461 | |
|---|
| 462 | filter_horizontal = ('tags',) |
|---|
| 463 | |
|---|
| 464 | .. image:: images/happy_tags.png |
|---|
| 465 | :scale: 70 |
|---|
| 466 | |
|---|
| 467 | ここまでで、blog/admin.pyは次のようになりました。 |
|---|
| 468 | :: |
|---|
| 469 | |
|---|
| 470 | class EntryModelAdmin(admin.ModelAdmin): |
|---|
| 471 | list_display = ['title', 'author', 'pub_date'] |
|---|
| 472 | fieldsets = ( |
|---|
| 473 | ('Core Information', { |
|---|
| 474 | 'fields': (('title', 'author'), 'body',) |
|---|
| 475 | }), |
|---|
| 476 | ('Options', { |
|---|
| 477 | 'classes': ['collapse',], |
|---|
| 478 | 'fields': (('pub_date', 'tags'), ) |
|---|
| 479 | }) |
|---|
| 480 | ) |
|---|
| 481 | list_filter = ['author', 'tags',] |
|---|
| 482 | date_hierarchy = 'pub_date' |
|---|
| 483 | search_fields = ('title, body',) |
|---|
| 484 | filter_horizontal = ('tags',) |
|---|
| 485 | |
|---|
| 486 | * Adminのラベルを日本語に切り替える |
|---|
| 487 | |
|---|
| 488 | Adminのみではなく、Djangoの国際化されている文字列が全ての日本語で表示されます。 |
|---|
| 489 | :: |
|---|
| 490 | |
|---|
| 491 | LANGUAGE_CODE = 'ja' |
|---|
| 492 | |
|---|
| 493 | |
|---|
| 494 | テンプレート |
|---|
| 495 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
|---|
| 496 | opendesignからもってきたテンプレートを使います。 |
|---|
| 497 | |
|---|
| 498 | * http://www.opendesigns.org/preview/?template=1182 |
|---|
| 499 | |
|---|
| 500 | .. image:: images/template_sample.png |
|---|
| 501 | :scale: 50 |
|---|
| 502 | |
|---|
| 503 | * 画面の各要素がブロックがどのページに表示されるべきかを検討する |
|---|
| 504 | |
|---|
| 505 | * 全ての画面に必要なもの |
|---|
| 506 | |
|---|
| 507 | .. image:: images/template_everypage.png |
|---|
| 508 | :scale: 50 |
|---|
| 509 | |
|---|
| 510 | * トップページにだけ必要なもの |
|---|
| 511 | |
|---|
| 512 | .. image:: images/template_toppage.png |
|---|
| 513 | :scale: 50 |
|---|
| 514 | |
|---|
| 515 | * 各ページで内容の変わるもの |
|---|
| 516 | |
|---|
| 517 | .. image:: images/template_content.png |
|---|
| 518 | :scale: 50 |
|---|
| 519 | |
|---|
| 520 | * Djangoのテンプレートを使う場合の定石に従ってテンプレートを構成する |
|---|
| 521 | |
|---|
| 522 | * 定石1 |
|---|
| 523 | |
|---|
| 524 | アプリケーションのモデル一覧・詳細はアプリケーションディレクトリに配備する |
|---|
| 525 | |
|---|
| 526 | 命名規則: 一覧 → APP/MODEL_list.html 、 詳細 → APP/MODEL_detail.html |
|---|
| 527 | |
|---|
| 528 | アプリケーションディレクトリ直下のtemplatesディレクトリがデフォルトでテンプレート格納ディレクトリになる。 |
|---|
| 529 | blogディレクトリ直下にtemplates/blogというディレクトリを作成する(templatesを作ってその中にblogを作る) |
|---|
| 530 | |
|---|
| 531 | **Entryモデルの一覧用** blog/templates/blog/entry_list.html |
|---|
| 532 | |
|---|
| 533 | **Entryモデルの章採用** blog/templates/blog/entry_detail.html |
|---|
| 534 | |
|---|
| 535 | * 定石2 |
|---|
| 536 | |
|---|
| 537 | 各テンプレートは同一階層のbase.htmlを継承し、base.htmlは上位階層のbase.htmlを継承する。 |
|---|
| 538 | |
|---|
| 539 | * 定石3 |
|---|
| 540 | |
|---|
| 541 | プロジェクトのテンプレートディレクトリを用意し、base.htmlを作成する |
|---|
| 542 | |
|---|
| 543 | * 実際の階層と、最初の状態は次のようになる |
|---|
| 544 | |
|---|
| 545 | :: |
|---|
| 546 | |
|---|
| 547 | #階層 |
|---|
| 548 | + hackathon |
|---|
| 549 | + templates |
|---|
| 550 | - base.html |
|---|
| 551 | + blog |
|---|
| 552 | + templates |
|---|
| 553 | + blog |
|---|
| 554 | - base.html |
|---|
| 555 | - entry_list.html |
|---|
| 556 | - entry_detail.html |
|---|
| 557 | |
|---|
| 558 | #blog/base.html |
|---|
| 559 | {% extends 'base.html' %} |
|---|
| 560 | |
|---|
| 561 | #blog/entry_list.html |
|---|
| 562 | {% extends 'blog/base.html' %} |
|---|
| 563 | |
|---|
| 564 | #entry_detail.html |
|---|
| 565 | {% extends 'blog/entry_detail.html %} |
|---|
| 566 | |
|---|
| 567 | * はやる気持ちを抑えつつ、静的ファイルの配信を準備する |
|---|
| 568 | |
|---|
| 569 | どこかにstaticという名前のディレクトリを作ってその中にstyle.cssとimagesを放り込む。 |
|---|
| 570 | |
|---|
| 571 | 次に、staticディレクトリ以下へのアクセスをdjango.views.static.serveに処理させる設定をプロジェクトのurls.pyに追加する。 |
|---|
| 572 | :: |
|---|
| 573 | |
|---|
| 574 | (r'^static/(?P<path>.*)$', 'django.views.static.serve', {'document_root': '/path/to/static'}), |
|---|
| 575 | |
|---|
| 576 | * プロジェクトのbase.htmlにindex.htmlテンプレートの中身をコピーする |
|---|
| 577 | |
|---|
| 578 | * imagesという文字列の前に {{ MEDIA_URL }} という文字列を記述する |
|---|
| 579 | |
|---|
| 580 | * styles.cssの前に {{ MEDIA_URL }} という文字列を記述する |
|---|
| 581 | |
|---|
| 582 | * settings.pyのMEDIA_URLの設定を '/static/' に変更する |
|---|
| 583 | |
|---|
| 584 | ※ MEDIA_URLはContextProcessorsがテンプレートに渡してくれます。 |
|---|
| 585 | |
|---|
| 586 | 現在設定されているContextProcessorsを確認するには |
|---|
| 587 | :: |
|---|
| 588 | |
|---|
| 589 | $ ./manage.py shell |
|---|
| 590 | >>> from django.conf import settings |
|---|
| 591 | >>> settings.TEMPLATE_CONTEXT_PROCESSORS |
|---|
| 592 | ('django.core.context_processors.auth', 'django.core.context_processors.debug', |
|---|
| 593 | 'django.core.context_processors.i18n', 'django.core.context_processors.media') |
|---|
| 594 | |
|---|
| 595 | http://code.djangoproject.com/browser/django/trunk/django/core/context_processors.py |
|---|
| 596 | :: |
|---|
| 597 | |
|---|
| 598 | def media(request): |
|---|
| 599 | """ |
|---|
| 600 | Adds media-related context variables to the context. |
|---|
| 601 | """ |
|---|
| 602 | return {'MEDIA_URL': settings.MEDIA_URL} |
|---|
| 603 | |
|---|
| 604 | どのページでも必要な情報を渡すのに便利。取得のコストが高いものはキャッシュを使っても良いね。 |
|---|
| 605 | |
|---|
| 606 | * プロジェクトテンプレートのbase.htmlにブロックを定義してゆく |
|---|
| 607 | |
|---|
| 608 | * 基本的なブロック |
|---|
| 609 | |
|---|
| 610 | * HTMLのheadを囲むように head という名前のブロック |
|---|
| 611 | |
|---|
| 612 | * headの中のtitleタグ内に title という名前のブロック |
|---|
| 613 | * headの終わりに extrahead という名前のブロック |
|---|
| 614 | |
|---|
| 615 | * footerが書いてあるdivを囲むように footer という名前のブロック |
|---|
| 616 | * メインコンテンツが書いてあるdivを囲むように content という名前のブロック |
|---|
| 617 | |
|---|
| 618 | * プラスアルファで必要なブロック |
|---|
| 619 | |
|---|
| 620 | * トップにのみ表示する画像を置く部分に gallery という名前のブロック |
|---|
| 621 | |
|---|
| 622 | |
|---|
| 623 | 実習 |
|---|
| 624 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
|---|
| 625 | * トップページの表示 |
|---|
| 626 | |
|---|
| 627 | / へのアクセスに対して、汎用ビューでEntryデータを20件表示する |
|---|
| 628 | |
|---|
| 629 | *ヒント* django.views.generic.date_based.archive_index |
|---|
| 630 | |
|---|
| 631 | * ユーザ毎一覧ページの表示 |
|---|
| 632 | |
|---|
| 633 | /USER/ へのアクセスに対して、汎用ビューをラップてユーザのEntryデータを20件表示する |
|---|
| 634 | |
|---|
| 635 | *ヒント* django.views.generic.date_based.archive_index か、django.views.generic.list_detail.object_list |
|---|
| 636 | |
|---|
| 637 | *ヒント* archive_indexの場合は、テンプレート名はAPP/MODEL_archive.html |
|---|
| 638 | |
|---|
| 639 | *ヒント* month_format='%m' |
|---|
| 640 | |
|---|
| 641 | ギャラリーのブロックを非表示にしてみる |
|---|
| 642 | |
|---|
| 643 | 余力があったらページングしてみる |
|---|
| 644 | |
|---|
| 645 | * エントリ詳細ページの表示 |
|---|
| 646 | |
|---|
| 647 | /USER/YEAR/MONTH/DAY/ID/ へのアクセスに対して、対象の日付の対象のデータのEntryデータを1件表示する |
|---|
| 648 | |
|---|
| 649 | *ヒント* django.views.generic.date_based.object_detail |
|---|
| 650 | |
|---|
| 651 | *ヒント* month_format='%m' |
|---|
| 652 | |
|---|
| 653 | 発展 |
|---|
| 654 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
|---|
| 655 | * 404/500ページを設定して、実際に表示させてみる(表示される条件を学ぶ) |
|---|
| 656 | * Commentフレームワーク(django.contrib.comments)を使ってエントリにコメントできるようにする |
|---|
| 657 | |
|---|
| 658 | * コメントが追加されたらメールをエントリの作者に送るようにする |
|---|
| 659 | |
|---|
| 660 | * コメントにコメントできるようにする |
|---|
| 661 | |
|---|
| 662 | * Akismetを使ってコメントスパムを防ぐ |
|---|
| 663 | |
|---|
| 664 | * 写真をアップできるようにする |
|---|
| 665 | |
|---|
| 666 | * トップページに最新の3枚を表示する |
|---|
| 667 | |
|---|
| 668 | * 写真を四角に切り取って、サイズもトップページ用に変換する |
|---|
| 669 | |
|---|
| 670 | * IDEFを保存できるようにしてみる |
|---|
| 671 | |
|---|
| 672 | * プロフィールを登録できるようにする |
|---|
| 673 | |
|---|
| 674 | * アイコンをアップロードできるようにする |
|---|
| 675 | |
|---|
| 676 | * Google Sitemapsを出力する |
|---|
| 677 | * テストを書く |
|---|
| 678 | * 公開してみる |
|---|
| 679 | |
|---|
| 680 | * CGI/FastCGI/mod_python/mod_wsgi |
|---|
| 681 | |
|---|
| 682 | あとは好きにしてくれ |
|---|