Django – マイグレーション操作

 

マイグレーションファイル作成

アプリケーションを作成・登録して、model.pyにモデルクラスを記述した後に実行。再実行の際は一度マイグレーションファイルを消した方がよい模様。

状態確認

マイグレーションの状態

マイグレーション実行のSQL表示

マイグレーション

未実行のマイグレーションを実行。

指定したアプリケーションとマイグレーションが既にマイグレーション済みの場合はロールバックになる点に注意。

ロールバック

指定したマイグレーションの手前まで

0001_initialも含めて全て

マイグレーションファイル削除

migrationsディレクトリー下のマイグレーションファイルとキャッシュを削除。

 

Django – settings.py – TEMPLATES

概要

settings.pyTEMPLATESは配列・辞書の入れ子になっている。

'DIRS'
プロジェクトルート直下のテンプレートディレクトリーを指定する。そのサブディレクトリーも探索される。
'APP_DIRS'
アプリケーションディレクトリー下のテンプレートディレクトリー探索を有効にする。そのサブディレクトリーも探索される。

Django – settings.pyへ。

テンプレートの探索場所の詳細についてはこちら

初期状態

settings.py生成直後のTEMPLATESの内容は以下のとおり。

 

'DIRS': []と設定されていて、プロジェクトルート(この場合はprojectroot/)直下は探索されない。

'APP_DIRS': Trueと設定されていて、各アプリケーションディレクトリー下のtemplatesディレクトリーが探索される。

DIRS

プロジェクトルート直下のテンプレートディレクトリーを指定する。たとえば以下のようにtemplatesディレクトリーを準備した場合。

準備したtemplatesを探索させるためには、以下のように設定する。

あるいは以下のようにベースディレクトリーからのパスであることを明示する。この場合settings.pyimport osが必要。

ディレクトリーの指定はサブディレクトリーでもよい。たとえばconfigディレクトリー下のtemplatesディレクトリーとしたければ以下のように記述。

APP_DIRS

APP_DIR=Trueとすると、各アプリケーションディレクトリー下のtemplatesディレクトリーが探索される。そのサブディレクトリーも探索される。

 

 

Django – settings.py

Djangoの設定ディレクトリー下に生成されるsettings.pyの内容を整理する。個別に設定想定していなければ、ここでは以下の構成を仮定している。

 

INSTALLED_APPS

TEMPLATES

DATABASES

国際化

 

Django – settings.py – 国際化

概要

settings.pyLANGUAGECODE変数で言語コードを、TIME_ZONE変数でタイムゾーンを指定する。

Django – settings.pyへ。

初期状態

settings.py生成直後の国際化関係の内容は以下のとおり。

日本語の場合の設定

言語コード

日本語に設定。

タイムゾーン

タイムゾーンを指定、有効化(初期状態のTrueのまま)。

Djangoは全ての日付=datetimeオブジェクトをUTCでデータベースに保存する。タイムゾーンの表現を任意に変更可能。

 

Django – settings.py – DATABASES

概要

Djangoでのデータベース設定は、settings.pyDATABASES辞書で設定する。

Django – settings.pyへ。

初期状態

settings.py生成直後のDATABASESの内容は以下のとおり。デフォルトでSQLiteが指定されている。

MySQLの場合の設定

MySQLを使う場合は以下のように記述。

ただし事前にmysqlクライアントのインストールが必要。

 

Django – ルーティング – urls.py

概要

リクエストされたURLから、Django内部で必要なview関数を呼ぶためのルーティングは、urls.pyで設定する。

  • urls.pyはプロジェクト設定ディレクトリーの下か、各アプリケーションディレクトリーの下
  • アプリケーションディレクトリー下のurls.pyは新規に作成し、設定ディレクトリーのurls.pyincludeする
  • ルーティングはurls.pyurlpatterns配列にpath関数で登録する
  • path関数の引数に、URLパターンとビュー関数を与える

ルーティングの流れについてはこちらも参照

設定ディレクトリー下のurls.py

プロジェクト構成

djangobasicsプロジェクトを作成。設定ディレクトリーをconfとし、アプリケーションurlincludeを作成・登録した場合のツリーは以下の通り。

ビューとテンプレート

urlinclude/views.pyを以下のように編集する。

urlinclude/templates/urlincludeディレクトリーを作成し、その下にindex.htmlを以下の内容で作成。

pathの登録

設定ディレクトリーconfの下のurls.pyに以下のように追記。以下、このurls.pyを「グローバルなurls.py」と呼ぶ。

まずアプリケーションのviewsモジュールをインポートしている。

そしてホスト名/アプリケーション名でindexビュー関数が呼ばれるようルーティング。

ここでhttp://localhost:8000/urlincludeでブラウザーからアクセスすると、index.htmlの内容が表示される。

アプリケーション下のurls.py

グローバルなurls.pyの問題点

設定ファイル直下のurls.pyでのpath登録は、

  • デフォルトで作成されたurls.pyを編集すればよい
  • 任意のアプリケーションのルーティング登録ができる

ただし以下のような問題点がある。

  • 登録するビューのアプリケーションをインポートする必要がある
  • 全てのビューを登録するため見通しが悪くなる
  • アプリケーション間でビューの名前の衝突が起こり得る

このため一般にはアプリケーションごとにurls.pyを作成し、そのモジュールを設定ディレクトリー下のグローバルなurls.pyincludeする。

ビューとテンプレート

ビューとテンプレートは先の例と同じものを使う。

アプリケーションのurls.pyの準備

アプリケーションディレクトリー下に、以下の内容でurls.pyファイルを作成する。conf/urls.pyのように自動作成されないので、新たに作成する必要がある。

ツリーは以下の通り。

グローバルなurls.pyの編集

conf/urls.pyを以下のように編集する。

  • django.urlsパッケージのpathモジュールに加えて、includeモジュールもインポートする
  • urlpatterns配列にpath関数を追加し、アプリケーションのurls.pyincludeする

ここでhttp://localhost:8000/urlinclude/indexにアクセスすると、index.htmlがブラウザーに表示される。

アプリケーションの登録

新たにアプリケーションを作成した場合には、プロジェクト共通ディレクトリーのsettings.pyINSTALLED_APPSに作成したアプリケーションを登録しておく必要がある。

留意点

グローバルなurls.pyでは、個々のアプリケーションのviewsモジュールをインポートする必要はない。

include関数の第1引数はアプリケーション名を使ったディレクトリーで、その後にアプリケーション内で定義したパスが続く。

  • 今回の例では'urlinclude/' + 'index''urlinclude/index'
  • 末尾に'/'がないとエラーになる

include関数の引数は、'アプリケーション.urls'の文字列。

  • 文字列にしていないと”名前が見つからない”、とエラーになる

直接テンプレートを表示する

path関数で直接テンプレートを表示させることもできるが、少し手間が必要。

  • urls.pyTemplateVierwクラスをインポート
  • path関数の第2引数にTemplateView.as_view()メソッドを与えてテンプレートを指定

たとえば以下は、index.htmlと同じディレクトリーにexample.htmlを作成し、これを直接path関数から呼び出す例。

 

Django – Troubleshooting

テンプレートがない

症状

ブラウザーで以下のエラー表示

TemplateDoesNotExist at /

原因

テンプレートはアプリケーション下なので探索範囲にある、アプリケーションのrender関数でもテンプレートのパスは間違っていない・・・。ほとんどの場合、settings.pyへのアプリケーションの登録漏れ。

 

名前が定義されていない

サーバーコンソールに以下のエラー表示

name *** is not defined

原因

引数のパスなどが文字列になっていないとこのエラーが出る。

 

Django – DTL – include

テンプレートの継承では、個別ページが共有するテンプレートを継承元の親テンプレートとした。一方、HTMLの一部を部品化して任意のページに埋め込むにはincludeタグを使う。

たとえば検索フォームを作成して、これを複数のページで使い回したいとする。例として以下のようなダミーのHTMLを準備する。

テンプレートの継承で使った以下のテンプレートを考える。includeタグで挿入するHTMLのパスを指定している。パスの指定の仕方は他のタグと同じ相対パス。

このページを呼び出すと、以下のようにincludeタグの部分に検索フォームのHTMLが埋め込まれる。

表示は以下の通りで、検索フォームの部分が表示されている。

ユーザー用メニュー

  • ユーザーホーム
  • ログアウト

ユーザーホームページ

ログイン済みユーザー用の内容

検索用のフォーム

 

Django – DTL – 継承

概要

個別ページで共通するHTMLの枠組みがある場合、それらを親テンプレートとして一つにまとめて、個別ページは親テンプレートを継承するようにできる。たとえばHTML全体の枠組みやサイトに共通するデザインなどをまとめるケース。テンプレートの継承の要点は以下のとおり。

  • 継承元の親テンプレートを準備
    • blockタグ→子テンプレートで具体内容を埋め込むブロック
  • 継承先の子テンプレートを準備
    • extends→継承元の親テンプレートを指定
    • blockタグ→親テンプレートのblockタグの部分に埋め込む内容
  • blockタグには識別名を書き、親・子でブロックを対応させる

子テンプレートを呼び出したとき、親テンプレートのblockタグの部分に子テンプレートのblockタグの内容が埋め込まれたHTMLとなる。

なお継承ではないが、includeタグによって別に準備したHTMLの一部分を取り込むことができる。

基本形

まず、子テンプレートで単に親テンプレートをextendsで読み込むだけの例を確認する。

継承元の親テンプレート

子テンプレートが継承する元の親テンプレート(layout.html)を以下のように準備する。要点は以下のとおり。

  • HTMLの完全な枠組みを持たせている
  • title要素、header要素、body要素にblockタグのブロックを書いている
    • title要素のblockには'Placeholder'という文字列を書いている
    • header要素のblockにはp要素とリストを書いている
    • body要素のblockには何も書いていない

子テンプレート

子テンプレート(index.html)ではextendsタグによって親テンプレートを継承する。この場合のタグの引数は、view関数で呼ぶrender文の第2引数と同じで、テンプレートディレクトリーからの相対パス。

子テンプレートはこの1行だけなので、単にこのテンプレートの内容が親テンプレートの内容で埋められる。

ただし親テンプレートのblockタグに対する子テンプレートのblockがないので、以下のように解釈される。

  • 親のblockに内容が定義されていれば、その内容が展開される
  • 親のblockがタグだけで内容がなければ、何も引き継がれない。

子テンプレートが引き継いだ親テンプレートの内容が解釈された結果、最終的なindex.htmlは以下のようになる。

  • title要素には'Placeholder'の文字が充てられる
  • header要素には、親で記述されたp要素とリストが埋められる
  • 親の{% block content %}のブロックには内容がないので、子要素にも何も引き継がれない

この結果、ブラウザーの表示は以下のようになる(ブラウザーのタブには'Placeholder'の文字が表示される。

Default Header Menu

  • Login
  • Signup

標準形

次に、子テンプレートindex.htmlで親テンプレートの各blockの具体の内容を実装する。

この結果、親テンプレートのblock部分に子テンプレートの対応するblockの内容が埋め込まれる。親テンプレートのblockに書かれた内容がある場合は、子テンプレートの内容で置き換えられる。

HTMLソースを確認すると、子テンプレートの内容が反映されていることがわかる。

ブラウザーでの表示結果は以下のとおり(ブラウザーのタブには’タイトル'と表示される)。

メニュー

  • ログイン
  • サインアップ

サイト内容

  • サイト紹介
  • 内容一覧

 

継承のチェーン

これまでは親~子の継承だったが、さらに親~子~孫の継承を見てみる。

たとえばページの種類が、認証済みユーザー用と非認証のゲスト用の2通りに区分され、それぞれでメニューの内容を変えたいとする。このような場合は、以下のようにテンプレートを分けて継承させる。

  • 親:全てのページに共通するテンプレート
  • 子:認証用/非認証用それぞれに応じたテンプレート
  • 孫:個々のページ~ページ内容によって認証用/非認証用あり

認証済みユーザー用のページ

まず、認証済みユーザー用のメニューを共通化し、これをユーザーホーム(index)で継承する例。先の例ではindex.htmllayout.htmlを継承したが、ここでは以下のように構成を変更する。

  • 前:layout.htmlindex.html
  • 後:layout.htmlauthenticated.htmlindex.html

サイト共通の基本的な枠組みはlayout.htmlで記述し、ユーザー認証中のHTML部分はauthenticated.htmlで、認証中の具体のページ内容をindex.htmlで実装している。

親テンプレートlayout.htmlはそのままとし、新たにauthenticated.htmlを以下の内容で準備する。ここでは{% block header %}のみを実装し、認証済みユーザー用のヘッダーメニューを表示させる。

index.htmlauthenticated.htmlを継承し、孫テンプレートとなる。未実装の{% block title %}{% block content %}を実装して「ログイン済みユーザー用の内容」とだけ表示させている。

index.htmlを呼び出すと、index.htmlcontenttitleが、authenticated.htmlheaderが実装され、以下のように表示される。

ユーザー用メニュー

  • ユーザーホーム
  • ログアウト

ユーザーホームページ

ログイン済みユーザー用の内容

 

非認証時のページ

次に非認証時のページとして、以下のような構成を追加する。

  • layout.htmlunauthenticated.htmllogin.html

全体共通の基本的な枠組みlayout.htmlは同じで、非認証時のHTML部分をuauthenticated.htmlにまとめ、非認証時の具体ページの例としてlogin.htmlを追加。

unauthenticated.htmlauthenticated.htmlと同じように{% block header %}のみを実装し、ゲスト用のメニューを記述。

ログインページでは非認証時のunauthenticated.htmlを継承し、具体のタイトルと内容を記述。

表示結果は以下のようになる。

ゲスト用メニュー

  • ログイン
  • サインアップ

ログインページ

ユーザーログイン用の内容

重複記述の禁止

blockタグの識別子が同じ名前で複数存在することはできない。たとえばtitle要素とページのh1要素が同じ内容なので以下のように2度使いしようとするとエラーになる。

 

Django – DTL – if

基本形

文法

基本はPythonのif ... elif ... elseと同じで、終了タグ{% endif %}で終える。

テンプレートの実例は以下の通り。

Pythonとの違い

以下はPythonのif文との違いで、外すとTemplateSyntaxErrorになる。

  • 条件部に()は使えない
    • {% if (he == gentle or she == tender) and they == love_each_other %} はエラー
    • こういう場合はifのネストにする
  • 条件部に算術演算子は使えないがフィルターは使える
    • {% if d % 2 == 2 %}はエラー
    • {% if d|divisable:2 %}はok

その他

ifequal/ifnotequal

if a == b/if a != bと同じ。

ifchanged

割愛。