Djangoの設定ディレクトリー下に生成されるsettings.py
の内容を整理する。個別に設定想定していなければ、ここでは以下の構成を仮定している。
1 2 3 4 5 6 7 8 |
projectroot/ ├── config │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── manage.py |
Djangoの設定ディレクトリー下に生成されるsettings.py
の内容を整理する。個別に設定想定していなければ、ここでは以下の構成を仮定している。
1 2 3 4 5 6 7 8 |
projectroot/ ├── config │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── manage.py |
settings.py
のLANGUAGECODE
変数で言語コードを、TIME_ZONE
変数でタイムゾーンを指定する。
settings.py
生成直後の国際化関係の内容は以下のとおり。
1 2 3 4 5 6 7 8 9 10 11 12 |
# Internationalization # https://docs.djangoproject.com/en/3.2/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True |
日本語に設定。
1 |
LANGUAGE_CODE = 'ja' |
タイムゾーンを指定、有効化(初期状態のTrue
のまま)。
1 2 3 |
TIME_ZONE = 'Asia/Tokyo' USE_TZ = True |
Djangoは全ての日付=datetime
オブジェクトをUTCでデータベースに保存する。タイムゾーンの表現を任意に変更可能。
Djangoでのデータベース設定は、settings.py
のDATABASES
辞書で設定する。
settings.py
生成直後のDATABASES
の内容は以下のとおり。デフォルトでSQLiteが指定されている。
1 2 3 4 5 6 7 8 9 |
# Database # https://docs.djangoproject.com/en/3.2/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } |
MySQLを使う場合は以下のように記述。
1 2 3 4 5 6 7 8 |
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'データベース名', 'USER': '接続ユーザー名', 'PASSWORD': 'パスワード', } } |
ただし事前にmysqlクライアントのインストールが必要。
1 |
$ pip3 install mysqlclient |
リクエストされたURLから、Django内部で必要なview関数を呼ぶためのルーティングは、urls.py
で設定する。
urls.py
はプロジェクト設定ディレクトリーの下か、各アプリケーションディレクトリーの下urls.py
は新規に作成し、設定ディレクトリーのurls.py
でinclude
するurls.py
のurlpatterns
配列にpath
関数で登録するpath
関数の引数に、URLパターンとビュー関数を与えるdjangobasics
プロジェクトを作成。設定ディレクトリーをconf
とし、アプリケーションurlinclude
を作成・登録した場合のツリーは以下の通り。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/djangobasics/ ├── conf │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── manage.py └── urlinclude ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py ├── urls.py └── views.py |
urlinclude/views.py
を以下のように編集する。
1 2 3 4 |
from django.shortcuts import render def index(request): return render(request, 'urlinclude/index.html') |
urlinclude/templates/urlinclude
ディレクトリーを作成し、その下にindex.html
を以下の内容で作成。
1 2 3 4 5 6 7 8 9 10 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>Index</title> </head> <body> <h1>Index</h1> </body> </html> |
設定ディレクトリーconf
の下のurls.py
に以下のように追記。以下、このurls.py
を「グローバルなurls.py
」と呼ぶ。
1 2 3 4 5 6 7 8 |
from django.contrib import admin from django.urls import path from urlinclude import views urlpatterns = [ path('admin/', admin.site.urls), path('urlinclude', views.index), ] |
まずアプリケーションのviewsモジュールをインポートしている。
1 |
from アプリケーション名 import views |
そしてホスト名/アプリケーション名
でindexビュー関数が呼ばれるようルーティング。
1 |
path('アプリケーション名', views.ビュー関数) |
ここでhttp://localhost:8000/urlinclude
でブラウザーからアクセスすると、index.html
の内容が表示される。
設定ファイル直下のurls.py
でのpath
登録は、
urls.py
を編集すればよいただし以下のような問題点がある。
このため一般にはアプリケーションごとにurls.py
を作成し、そのモジュールを設定ディレクトリー下のグローバルなurls.py
でinclude
する。
ビューとテンプレートは先の例と同じものを使う。
アプリケーションディレクトリー下に、以下の内容でurls.py
ファイルを作成する。conf/urls.py
のように自動作成されないので、新たに作成する必要がある。
1 2 3 4 5 6 |
from django.urls import path from urlinclude import views urlpatterns = [ path('index', views.index), ] |
ツリーは以下の通り。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/djangobasics/ ├── conf │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── manage.py ├── startserver.sh └── urlinclude ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ ├── __init__.py │ └── __pycache__ │ └── __init__.cpython-36.pyc ├── models.py ├── templates │ └── urlinclude │ └── index.html ├── tests.py ├── urls.py └── views.py |
conf/urls.py
を以下のように編集する。
django.urls
パッケージのpath
モジュールに加えて、include
モジュールもインポートするurlpatterns
配列にpath
関数を追加し、アプリケーションのurls.py
をinclude
する
1 2 3 4 5 6 7 8 9 |
from django.contrib import admin from django.urls import path, include #from urlinclude import views urlpatterns = [ path('admin/', admin.site.urls), # path('urlinclude/', views.index), path('urlinclude/', include('urlinclude.urls')), ] |
ここでhttp://localhost:8000/urlinclude/index
にアクセスすると、index.html
がブラウザーに表示される。
新たにアプリケーションを作成した場合には、プロジェクト共通ディレクトリーのsettings.py
でINSTALLED_APPS
に作成したアプリケーションを登録しておく必要がある。
1 2 3 4 |
INSTALLED_APPS = [ ...., 'urlinclude.apps.UrlincludeConfig', ] |
グローバルなurls.py
では、個々のアプリケーションのviewsモジュールをインポートする必要はない。
include
関数の第1引数はアプリケーション名を使ったディレクトリーで、その後にアプリケーション内で定義したパスが続く。
'urlinclude/'
+ 'index'
→'urlinclude/index'
'/'
がないとエラーになるinclude
関数の引数は、'アプリケーション.urls'
の文字列。
path
関数で直接テンプレートを表示させることもできるが、少し手間が必要。
urls.py
でTemplateVierw
クラスをインポートpath
関数の第2引数にTemplateView.as_view()
メソッドを与えてテンプレートを指定たとえば以下は、index.html
と同じディレクトリーにexample.html
を作成し、これを直接path
関数から呼び出す例。
1 2 3 4 5 6 7 8 |
from django.urls import path from urlinclude import views from django.views.generic.base import TemplateView urlpatterns = [ path('index', views.index), path('example', TemplateView.as_view(template_name='urlinclude/example.html')), ] |
ブラウザーで以下のエラー表示
TemplateDoesNotExist at /
テンプレートはアプリケーション下なので探索範囲にある、アプリケーションのrender関数でもテンプレートのパスは間違っていない・・・。ほとんどの場合、settings.py
へのアプリケーションの登録漏れ。
サーバーコンソールに以下のエラー表示
name *** is not defined
引数のパスなどが文字列になっていないとこのエラーが出る。
1 2 3 4 5 6 7 |
render(request, app/index.html) ↓ render(request, 'app/index.html') path(app/, include(app.urls) ↓ path(app/, include('app.urls') |
テンプレートの継承では、個別ページが共有するテンプレートを継承元の親テンプレートとした。一方、HTMLの一部を部品化して任意のページに埋め込むにはinclude
タグを使う。
たとえば検索フォームを作成して、これを複数のページで使い回したいとする。例として以下のようなダミーのHTMLを準備する。
1 2 3 |
div> <p>検索用のフォーム</p> </div> |
テンプレートの継承で使った以下のテンプレートを考える。include
タグで挿入するHTMLのパスを指定している。パスの指定の仕方は他のタグと同じ相対パス。
1 2 3 4 5 6 7 8 9 10 11 12 |
{% extends 'inheritance/authenticated.html' %} {% block title %}ようこそ{% endblock %} {% block content %} <h1>ユーザーホームページ</h1> <p>ログイン済みユーザー用の内容</p> {% include 'inheritance/search.html' %} {% endblock %} |
このページを呼び出すと、以下のようにinclude
タグの部分に検索フォームのHTMLが埋め込まれる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>ようこそ</title> </head> <body> <p>ユーザー用メニュー</p> <ul> <li>ユーザーホーム</li> <li>ログアウト</li> </ul> <p>ユーザーホームページ</p> <p>ログイン済みユーザー用の内容</p> <div> <p>検索用のフォーム</p> </div> </body> </html> |
表示は以下の通りで、検索フォームの部分が表示されている。
ユーザー用メニュー
ユーザーホームページ
ログイン済みユーザー用の内容
検索用のフォーム
個別ページで共通するHTMLの枠組みがある場合、それらを親テンプレートとして一つにまとめて、個別ページは親テンプレートを継承するようにできる。たとえばHTML全体の枠組みやサイトに共通するデザインなどをまとめるケース。テンプレートの継承の要点は以下のとおり。
block
タグ→子テンプレートで具体内容を埋め込むブロックextends
→継承元の親テンプレートを指定block
タグ→親テンプレートのblock
タグの部分に埋め込む内容block
タグには識別名を書き、親・子でブロックを対応させる子テンプレートを呼び出したとき、親テンプレートのblock
タグの部分に子テンプレートのblock
タグの内容が埋め込まれたHTMLとなる。
なお継承ではないが、include
タグによって別に準備したHTMLの一部分を取り込むことができる。
まず、子テンプレートで単に親テンプレートをextends
で読み込むだけの例を確認する。
子テンプレートが継承する元の親テンプレート(layout.html
)を以下のように準備する。要点は以下のとおり。
title
要素、header
要素、body
要素にblock
タグのブロックを書いている
title
要素のblock
には'Placeholder'
という文字列を書いているheader
要素のblock
にはp
要素とリストを書いているbody
要素のblock
には何も書いていない
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>{% block title %}Placeholeder{% endblock %}</title> </head> <body> <header> {% block header %} <p>Default Header Menu</p> <ul> <li>Login</li> <li>Signup</li> </ul> {% endblock %} </header> {% block content %} {% endblock %} </body> </html> |
子テンプレート(index.html
)ではextends
タグによって親テンプレートを継承する。この場合のタグの引数は、view関数で呼ぶrender
文の第2引数と同じで、テンプレートディレクトリーからの相対パス。
1 |
{% extends 'inheritance/layout.html' %} |
子テンプレートはこの1行だけなので、単にこのテンプレートの内容が親テンプレートの内容で埋められる。
ただし親テンプレートのblock
タグに対する子テンプレートのblock
がないので、以下のように解釈される。
block
に内容が定義されていれば、その内容が展開されるblock
がタグだけで内容がなければ、何も引き継がれない。子テンプレートが引き継いだ親テンプレートの内容が解釈された結果、最終的なindex.html
は以下のようになる。
title
要素には'Placeholder'
の文字が充てられるheader
要素には、親で記述されたp
要素とリストが埋められる{% block content %}
のブロックには内容がないので、子要素にも何も引き継がれない
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>Placeholeder</title> </head> <body> <header> <p>Default Header Menu</p> <ul> <li>Login</li> <li>Signup</li> </ul> </header> </body> </html> |
この結果、ブラウザーの表示は以下のようになる(ブラウザーのタブには'Placeholder'
の文字が表示される。
Default Header Menu
次に、子テンプレートindex.html
で親テンプレートの各block
の具体の内容を実装する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
{% extends 'inheritance/layout.html' %} {% block title %}タイトル{% endblock %} {% block header %} <p>メニュー</p> <ul> <li>ログイン</li> <li>サインアップ</li> </ul> {% endblock %} {% block content %} <p>サイト内容</p> <ul> <li>サイト紹介</li> <li>内容一覧</li> </ul> {% endblock %} |
この結果、親テンプレートのblock部分に子テンプレートの対応するblockの内容が埋め込まれる。親テンプレートのblockに書かれた内容がある場合は、子テンプレートの内容で置き換えられる。
HTMLソースを確認すると、子テンプレートの内容が反映されていることがわかる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>タイトル</title> </head> <body> <header> <p>メニュー</p> <ul> <li>ログイン</li> <li>サインアップ</li> </ul> </header> <p>サイト内容</p> <ul> <li>サイト紹介</li> <li>内容一覧</li> </ul> </body> </html> |
ブラウザーでの表示結果は以下のとおり(ブラウザーのタブには’タイトル'
と表示される)。
サイト内容
これまでは親~子の継承だったが、さらに親~子~孫の継承を見てみる。
たとえばページの種類が、認証済みユーザー用と非認証のゲスト用の2通りに区分され、それぞれでメニューの内容を変えたいとする。このような場合は、以下のようにテンプレートを分けて継承させる。
まず、認証済みユーザー用のメニューを共通化し、これをユーザーホーム(index)で継承する例。先の例ではindex.html
がlayout.html
を継承したが、ここでは以下のように構成を変更する。
layout.html
←index.html
layout.html
←authenticated.html
←index.html
サイト共通の基本的な枠組みはlayout.html
で記述し、ユーザー認証中のHTML部分はauthenticated.html
で、認証中の具体のページ内容をindex.html
で実装している。
親テンプレートlayout.html
はそのままとし、新たにauthenticated.html
を以下の内容で準備する。ここでは{% block header %}
のみを実装し、認証済みユーザー用のヘッダーメニューを表示させる。
1 2 3 4 5 6 7 8 9 |
{% extends 'inheritance/layout.html' %} {% block header %} <p>ユーザー用メニュー</p> <ul> <li>ユーザーホーム</li> <li>ログアウト</li> </ul> {% endblock %} |
index.html
はauthenticated.html
を継承し、孫テンプレートとなる。未実装の{% block title %}
と{% block content %}
を実装して「ログイン済みユーザー用の内容」とだけ表示させている。
1 2 3 4 5 6 7 8 9 |
{% extends 'inheritance/authenticated.html' %} {% block title %}ようこそ{% endblock %} {% block content %} <h1>ユーザーホームページ</h1> <p>ログイン済みユーザー用の内容</p> {% endblock %} |
index.html
を呼び出すと、index.html
でcontent
とtitle
が、authenticated.html
でheader
が実装され、以下のように表示される。
ユーザー用メニュー
ユーザーホームページ
ログイン済みユーザー用の内容
次に非認証時のページとして、以下のような構成を追加する。
layout.html
←unauthenticated.html
←login.html
全体共通の基本的な枠組みlayout.html
は同じで、非認証時のHTML部分をuauthenticated.html
にまとめ、非認証時の具体ページの例としてlogin.html
を追加。
unauthenticated.html
はauthenticated.html
と同じように{% block header %}
のみを実装し、ゲスト用のメニューを記述。
1 2 3 4 5 6 7 8 9 |
{% extends 'inheritance/layout.html' %} {% block header %} <p>ゲスト用メニュー</p> <ul> <li>ログイン</li> <li>サインアップ</li> </ul> {% endblock %} |
ログインページでは非認証時のunauthenticated.html
を継承し、具体のタイトルと内容を記述。
1 2 3 4 5 6 7 8 9 |
{% extends 'inheritance/unauthenticated.html' %} {% block title %}ログイン{% endblock %} {% block content %} <h1>ログインページ</h1> <p>ユーザーログイン用の内容</p> {% endblock %} |
表示結果は以下のようになる。
ゲスト用メニュー
ログインページ
ユーザーログイン用の内容
block
タグの識別子が同じ名前で複数存在することはできない。たとえばtitle
要素とページのh1
要素が同じ内容なので以下のように2度使いしようとするとエラーになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>{% block title %}Placeholeder{% endblock %}</title> </head> <body> {% comment %} <h1>{% block title %}Title Here{% endblock %}</h1> 同じブロック名のブロックは重複して書けない TemplateSyntaxError at /inheritance 'block' tag with name 'title' appears more than once {% endcomment %} </body> </html> |
基本はPythonのif ... elif ... else
と同じで、終了タグ{% endif %}
で終える。
1 2 3 4 5 6 7 |
{% if .... %} .... {% elif %} .... {% else %} .... {% endif %} |
テンプレートの実例は以下の通り。
1 2 3 4 5 6 7 8 9 10 |
<p> {{ var }} is {% if var > 0 %} Poisive. {% elif var < 0 %} Negative. {% else %} Zero. {% endif %} </p> |
以下はPythonのif
文との違いで、外すとTemplateSyntaxErrorになる。
()
は使えない
{% if (he == gentle or she == tender) and they == love_each_other %}
はエラーif
のネストにする{% if d % 2 == 2 %}
はエラー{% if d|divisable:2 %}
はokif a == b
/if a != b
と同じ。
割愛。
DjangoのDTLにおけるフィルターの一般形式は以下のようになっている。
変数|フィルター:引数|フィルター:引数|・・・
フィルターは変数展開{{ .... }}
でもタグ{% .... %}
でも使えるが、それぞれで'|'
や':'
の前後にスペースを入れた時の挙動が異なるので整理する。
結論すると、DTLでフィルターを使う場合は'|'
と':'
の前後にはスペースを入れないと決めておくのが紛れがなくてよい。
'|'
の前後のスペースは許されるが、':'
の前後にスペースを入れるとTemplateSyntaxErrorになる。
{{ var|add:2 }} |
Ok |
{{ var |add:2 }} |
Ok |
{{ var| add:2 }} |
Ok |
{{ var|add :2 }} |
Error |
{{ var|add: 2 }} |
Error |
'|'
の前後も':'
の前後もスペースを入れるとTemplateSyntaxErrorになる。
{% if var|divisibleby:2 %} |
Ok |
{% if var |divisibleby:2 %} |
Error |
{% if var| divisibleby:2 %} |
Error |
{% if var|divisibleby :2 %} |
Error |
{% if var|divisibleby: 2 %} |
Error |
Djangoのテンプレート言語(DTL: Django Template Language)について整理する。
一行コメント{#...#}
と複数行コメント{% comment %}...{% endcomment %}
がある。
変数の内容を展開するには{{ 変数 }}
と記述する。
変数の内容は、数値・文字列のほか、コレクションの要素や一般のオブジェクトの属性やメソッドの結果も展開可能。
DTLのタグは{% タグ %}
と記述する。
主なタグには以下のようなものがある。
特殊機能
comment
:複数行コメントcsrf_token
:CSRF (Cross Site Request Forgeries)対策用制御
URL指定