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.htmllayout.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指定