「Django 最初のアプリケーション 3」の版間の差分
ナビゲーションに移動
検索に移動
(ページの作成:「==Django 最初のアプリケーション 3 (Viewの作成)== [Django 最初のアプリケーション 4]] Django][Python][前|Django 最初のアプリケーショ…」) |
|||
(同じ利用者による、間の3版が非表示) | |||
1行目: | 1行目: | ||
− | ==Django 最初のアプリケーション 3 (Viewの作成)== | + | ==[[Django 最初のアプリケーション 3]] (Viewの作成)== |
− | [ | + | [Django][Python][[Django 最初のアプリケーション 4]|[前|Django 最初のアプリケーション 2]]][[[次]] |
− | + | [[Python]]の概要も分かり易い. | |
{{amazon|477413760X}} | {{amazon|477413760X}} | ||
11行目: | 11行目: | ||
===方針=== | ===方針=== | ||
− | + | <blockquote>[[Django]]アプリケーションにて、ビューは、Webページの『型』であり、特定の機能や、テンプレートを提供する。</blockquote> | |
=====たとえば、ブログアプリケーションでは以下のようなビュー。===== | =====たとえば、ブログアプリケーションでは以下のようなビュー。===== | ||
*ホームページ - 最新の2、3のエントリを表示 | *ホームページ - 最新の2、3のエントリを表示 | ||
− | *エントリの詳細ページ - | + | *エントリの詳細ページ - あるエントリへのパーマ[[リンク]] |
*年別アーカイブページ - ある年のエントリを月別表示 | *年別アーカイブページ - ある年のエントリを月別表示 | ||
*月別アーカイブページ - ある月のエントリを日別表示 | *月別アーカイブページ - ある月のエントリを日別表示 | ||
26行目: | 26行目: | ||
*投票ページ - 特定の調査に対する投票を選択する | *投票ページ - 特定の調査に対する投票を選択する | ||
− | + | <blockquote>Djangoではビューをシンプルな [[Python]]の関数で表現する</blockquote> | |
− | === | + | ===U[[R]]Lの設計=== |
− | ==== | + | ====U[[R]]Lの構造を設計==== |
− | * | + | *最初に、U[[R]]Lの構造を設計する |
− | * | + | *これには、URLconfから呼び出される、[[Python]]のモジュールを作成する |
− | * | + | *URLconfは、DjangoがURLと[[Python]]コードを結びつける |
− | * | + | *[[Django]]で作成されたページにアクセスすると、システムは ROOT_URLCONF設定を探す |
− | * | + | *ROOT_URLCONFは[[Python]]のドット区切り文法で記述された文字列 |
− | * | + | *[[Django]]はこのモジュールをロードし、呼び出されたURLパターンで、モジュールレベル変数を探す |
− | *URLパターンは、以下の書式の[Python サンプルコード] | + | *URLパターンは、以下の書式の[[Python サンプルコード|タプル]] |
(正規表現, Python コールバック関数 [, オプションのディクショナリ]) | (正規表現, Python コールバック関数 [, オプションのディクショナリ]) | ||
− | * | + | *[[Django]]は、最初に正規表現が一致するまでリストを下っていく |
− | *一致するものが見つかったら、Djangoは、HttpRequestオブジェクトを最初の引数として、キーワード引数として、正規表現から捕捉された値、オプションで任意のディクショナリ(タプルの3つ目の項目) | + | *一致するものが見つかったら、Djangoは、HttpRequestオブジェクトを最初の引数として、キーワード引数として、正規表現から捕捉された値、オプションで任意のディクショナリ(タプルの3つ目の項目)を伴って、[[Python]]コールバック関数を呼び出す |
====オブジェクト詳細==== | ====オブジェクト詳細==== | ||
49行目: | 49行目: | ||
====urls.pyの編集==== | ====urls.pyの編集==== | ||
− | *[Django 最初のアプリケーション 2 | + | *[[Django 最初のアプリケーション 2|最初のアプリケーション 1]]で、django-admin.py startproject mysite を実行したとき、mysite/urls.py にデフォルトの URLconfを作成している。 |
− | *自動的に、settings. | + | *自動的に、settings.pyファイルの[[R]]OOT_U[[R]]LCONF 設定 にて、そのファイルを指定している |
− | + | [[R]]OOT_U[[R]]LCONF = 'mysite.urls' | |
*mysite/urls.pyを以下のように編集 | *mysite/urls.pyを以下のように編集 | ||
63行目: | 63行目: | ||
# Example: | # Example: | ||
# (r'^mysite/', include('mysite.foo.urls')), | # (r'^mysite/', include('mysite.foo.urls')), | ||
− | (r'^polls/$', 'mysite.polls. | + | (r'^polls/$', 'mysite.polls.[[vi]]ews.index'), |
− | (r'^polls/(?P | + | (r'^polls/(?P<poll_id>\d+)/$', 'mysite.polls.[[vi]]ews.detail'), |
− | (r'^polls/(?P | + | (r'^polls/(?P<poll_id>\d+)/results/$', 'mysite.polls.[[vi]]ews.results'), |
− | (r'^polls/(?P | + | (r'^polls/(?P<poll_id>\d+)/vote/$', 'mysite.polls.[[vi]]ews.vote'), |
# Uncomment the admin/doc line below and add 'django.contrib.admindocs' | # Uncomment the admin/doc line below and add 'django.contrib.admindocs' | ||
# to INSTALLED_APPS to enable admin documentation: | # to INSTALLED_APPS to enable admin documentation: | ||
− | # (r'^admin/doc/', include('dhttp://typea.info/tips/wiki.cgi?action=EDIT&page=Django+%BA%C7%BD%E9%A4%CE%A5%A2%A5%D7%A5%EA%A5%B1%A1%BC%A5%B7%A5%E7%A5%F3+3&artno=2jango.contrib.admindocs.urls')), | + | # (r'^admin/doc/', include('dhttp://typea.info/tips/wiki.cgi?action=EDIT&page=[[Django]]+%BA%C7%BD%E9%A4%CE%A5%A2%A5%D7%A5%EA%A5%B1%A1%BC%A5%B7%A5%E7%A5%F3+3&artno=2jango.contrib.admindocs.urls')), |
# Uncomment the next line to enable the admin: | # Uncomment the next line to enable the admin: | ||
77行目: | 77行目: | ||
====リクエストパラメータの解析==== | ====リクエストパラメータの解析==== | ||
− | 上記の例では、"/polls/23/" | + | 上記の例では、"/polls/23/"というリクエストがあった場合、Djangoは[[Python]]モジュールをロードする。これは、r'^polls/(?P<poll_id>\d+)/$' にマッチするためであり、 mysite/polls/views.py.からdetail()関数が以下のように呼び出される。 |
− | detail(request= | + | detail(request=<Http[[R]]equest object>, poll_id='23') |
− | *poll_id='23' の部分は、(?P | + | *poll_id='23' の部分は、(?P<poll_id>\d+)から来ている |
*丸括弧でパターンをを囲うことにより、テキストの一致を『捕捉』しビュー関数に引数として送信する。 | *丸括弧でパターンをを囲うことにより、テキストの一致を『捕捉』しビュー関数に引数として送信する。 | ||
− | *?P | + | *?P<poll_id> がパターンにマッチした場合の名称を定義している。 |
− | *\d+ | + | *\d+ は[[正規表現]]で数字の連続 |
===最初のビュー=== | ===最初のビュー=== | ||
90行目: | 90行目: | ||
[[File:0372_django_firstapp24.jpg]] | [[File:0372_django_firstapp24.jpg]] | ||
− | *これは、index() 関数を mysite/polls/ | + | *これは、index() 関数を mysite/polls/[[vi]]ews.py. に実装していないため。 |
====シンプルビューの実装==== | ====シンプルビューの実装==== | ||
− | *mysite/polls/ | + | *mysite/polls/[[vi]]ews.py に以下を記述してみる |
− | from django.http import | + | from django.http import Http[[R]]esponse |
def index(request): | def index(request): | ||
− | return | + | return Http[[R]]esponse("Hello, world. You're at the poll index.") |
[[File:0373_django_firstapp25.jpg]] | [[File:0373_django_firstapp25.jpg]] | ||
104行目: | 104行目: | ||
*引数をとる以下の関数を追加し、http://192.168.24.14:8080/polls/34/ にアクセス | *引数をとる以下の関数を追加し、http://192.168.24.14:8080/polls/34/ にアクセス | ||
def detail(request, poll_id): | def detail(request, poll_id): | ||
− | return | + | return Http[[R]]esponse("You're looking at poll %s." % poll_id) |
[[File:0374_django_firstapp26.jpg]] | [[File:0374_django_firstapp26.jpg]] | ||
− | + | U[[R]]Lに入力したパラメータを取得できている | |
===実際になにかするビュー=== | ===実際になにかするビュー=== | ||
====簡単なビューの作成==== | ====簡単なビューの作成==== | ||
*ビューは次の2つのうちどちらかを行う責任がある。 | *ビューは次の2つのうちどちらかを行う責任がある。 | ||
− | ** | + | **リクエストされたページのコンテンツを含んだHttp[[R]]esponseオブジェクトを返す |
**Http404のようなエラーを返す | **Http404のようなエラーを返す | ||
− | * | + | *先ほどの例で、HttpResponseにメッセージを直接与えたように、Pollの最新の5件を出力するようにハードコーディングしてしまうと、ページの見た目を変えるために[[Python]]のコードに手を加えなければいけなくなってしまう。[[Python]]からデザインを分離するために、テンプレートを使用する。 |
− | *以下のコードはpolls/index. | + | *以下のコードはpolls/index.htmlテンプレートをロードし、Contextというテンプレート変数名と[[Python]]オブジェクトのディクショナリを引き渡す |
from django.template import Context, loader | from django.template import Context, loader | ||
from mysite.polls.models import Poll | from mysite.polls.models import Poll | ||
− | from django.http import | + | from django.http import Http[[R]]esponse |
def index(request): | def index(request): | ||
127行目: | 127行目: | ||
'latest_poll_list': latest_poll_list | 'latest_poll_list': latest_poll_list | ||
}) | }) | ||
− | return | + | return Http[[R]]esponse(t.render(c)) |
*ページをリロードするとテンプレートがまだないため、以下のようなエラーとなる | *ページをリロードするとテンプレートがまだないため、以下のようなエラーとなる | ||
133行目: | 133行目: | ||
polls/index.html | polls/index.html | ||
− | *[Django 最初のアプリケーション 2 | + | *[[Django 最初のアプリケーション 2|最初のアプリケーション 2]]でsetting.pyに指定したTEMPLATE_DIRS のディレクトリ以下に、pollsディレクトリを作成し、index.htmlファイルを作成する |
*loader.get_template('polls/index.html') は、"[テンプレートディレクトリ]/polls/index.html"をファイルシステム上では指すようになる | *loader.get_template('polls/index.html') は、"[テンプレートディレクトリ]/polls/index.html"をファイルシステム上では指すようになる | ||
'''index.html''' | '''index.html''' | ||
{% if latest_poll_list %} | {% if latest_poll_list %} | ||
− | + | <ul> | |
{% for poll in latest_poll_list %} | {% for poll in latest_poll_list %} | ||
− | + | <li>{{ poll.question }}</li> | |
{% endfor %} | {% endfor %} | ||
− | + | </ul> | |
{% else %} | {% else %} | ||
− | + | <p>No polls are available.</p> | |
{% endif %} | {% endif %} | ||
[[File:0375_django_firstapp27.jpg]] | [[File:0375_django_firstapp27.jpg]] | ||
=====ショートカット : render_to_response()===== | =====ショートカット : render_to_response()===== | ||
− | * | + | *テンプレートをロードするのによく使われる方法は、コンテキストとHttp[[R]]esponseオブジェクトをテンプレートとともに詰めて返す。 |
書き直してみる | 書き直してみる | ||
170行目: | 170行目: | ||
raise Http404 | raise Http404 | ||
return render_to_response('polls/detail.html', { 'poll': p }) | return render_to_response('polls/detail.html', { 'poll': p }) | ||
− | *Poll | + | *Poll のIDが見つからない場合、[[HTTP]] 404エラーを返す |
*detail.htmlにとりあえず、以下の内容を記述 | *detail.htmlにとりあえず、以下の内容を記述 | ||
181行目: | 181行目: | ||
=====ショートカット : get_object_or_404()===== | =====ショートカット : get_object_or_404()===== | ||
− | * | + | *[[Django]]はこのような場合にもショートカットを提供 |
from django.shortcuts import render_to_response, get_object_or_404 | from django.shortcuts import render_to_response, get_object_or_404 | ||
from mysite.polls.models import Poll | from mysite.polls.models import Poll | ||
192行目: | 192行目: | ||
====テンプレートシステムを利用する==== | ====テンプレートシステムを利用する==== | ||
*detail()ビューは、次のような感じ | *detail()ビューは、次のような感じ | ||
− | + | <h1>{{ poll.question }}</h1> | |
− | + | <ul> | |
{% for choice in poll.choice_set.all %} | {% for choice in poll.choice_set.all %} | ||
− | + | <li>{{ choice.choice }}</li> | |
{% endfor %} | {% endfor %} | ||
− | + | </ul> | |
[[File:0378_django_firstapp30.jpg]] | [[File:0378_django_firstapp30.jpg]] | ||
*テンプレートでは、ドット記法で、変数の属性にアクセスできる。{{ poll.question }}のように | *テンプレートでは、ドット記法で、変数の属性にアクセスできる。{{ poll.question }}のように | ||
*詳しくは[http://docs.djangoproject.com/en/dev/topics/templates/#topics-templates テンプレートガイド]参照 | *詳しくは[http://docs.djangoproject.com/en/dev/topics/templates/#topics-templates テンプレートガイド]参照 | ||
− | === | + | ===U[[R]]Lconfs=== |
====簡単にする==== | ====簡単にする==== | ||
*各コールバック関数共通の、「mysite.polls.vews」をプレフィックスとして外だしできる | *各コールバック関数共通の、「mysite.polls.vews」をプレフィックスとして外だしできる | ||
urlpatterns = patterns('mysite.polls.vews', | urlpatterns = patterns('mysite.polls.vews', | ||
(r'^polls/$', 'index'), | (r'^polls/$', 'index'), | ||
− | (r'^polls/(?P | + | (r'^polls/(?P<poll_id>\d+)/$', 'detail'), |
− | (r'^polls/(?P | + | (r'^polls/(?P<poll_id>\d+)/results/$', 'results'), |
− | (r'^polls/(?P | + | (r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'), |
) | ) | ||
====分割する==== | ====分割する==== | ||
220行目: | 220行目: | ||
) | ) | ||
=====mysite/polls/urls.py===== | =====mysite/polls/urls.py===== | ||
− | * | + | *[[正規表現]]から、polls/ を取り除く。 |
− | *こうすることにより、/polls/ | + | *こうすることにより、/polls/から、ほかのU[[R]]Lへの移動も簡単になる |
− | urlpatterns = patterns('mysite.polls. | + | urlpatterns = patterns('mysite.polls.[[vi]]ews', |
(r'^$', 'index'), | (r'^$', 'index'), | ||
− | (r'^(?P | + | (r'^(?P<poll_id>\d+)/$', 'detail'), |
− | (r'^(?P | + | (r'^(?P<poll_id>\d+)/results/$', 'results'), |
− | (r'^(?P | + | (r'^(?P<poll_id>\d+)/vote/$', 'vote'), |
) | ) | ||
− | [Django 最初のアプリケーション 4] | + | [[Django 最初のアプリケーション 4]|[前|Django 最初のアプリケーション 2]]][[[次]] |
2020年2月16日 (日) 04:24時点における最新版
Django 最初のアプリケーション 3 (Viewの作成)
[Django][Python][[Django 最初のアプリケーション 4]|[前|Django 最初のアプリケーション 2]]][[[次]]
Pythonの概要も分かり易い.
を参考にサンプルアプリケーションを作成してみる
方針
<blockquote>Djangoアプリケーションにて、ビューは、Webページの『型』であり、特定の機能や、テンプレートを提供する。</blockquote>
たとえば、ブログアプリケーションでは以下のようなビュー。
- ホームページ - 最新の2、3のエントリを表示
- エントリの詳細ページ - あるエントリへのパーマリンク
- 年別アーカイブページ - ある年のエントリを月別表示
- 月別アーカイブページ - ある月のエントリを日別表示
- 日別アーカイブページ - ある日のエントリを表示
- コメントページ - エントリへのコメント
サンプルとして作成している Poll アプリケーションでは、以下の4つのビューを持たせる
- アーカイブページ - 最新の2、3の調査を表示
- 詳細ページ - 調査の質問を表示。結果は表示せず、投票フォームを表示
- 結果ページ - 調査結果のページ特定の調査についての結果
- 投票ページ - 特定の調査に対する投票を選択する
<blockquote>Djangoではビューをシンプルな Pythonの関数で表現する</blockquote>
URLの設計
URLの構造を設計
- Djangoで作成されたページにアクセスすると、システムは ROOT_URLCONF設定を探す
- ROOT_URLCONFはPythonのドット区切り文法で記述された文字列
- Djangoはこのモジュールをロードし、呼び出されたURLパターンで、モジュールレベル変数を探す
- URLパターンは、以下の書式のタプル
(正規表現, Python コールバック関数 [, オプションのディクショナリ])
- Djangoは、最初に正規表現が一致するまでリストを下っていく
- 一致するものが見つかったら、Djangoは、HttpRequestオブジェクトを最初の引数として、キーワード引数として、正規表現から捕捉された値、オプションで任意のディクショナリ(タプルの3つ目の項目)を伴って、Pythonコールバック関数を呼び出す
オブジェクト詳細
urls.pyの編集
- 最初のアプリケーション 1で、django-admin.py startproject mysite を実行したとき、mysite/urls.py にデフォルトの URLconfを作成している。
- 自動的に、settings.pyファイルのROOT_URLCONF 設定 にて、そのファイルを指定している
ROOT_URLCONF = 'mysite.urls'
- mysite/urls.pyを以下のように編集
from django.conf.urls.defaults import * # Uncomment the next two lines to enable the admin: from django.contrib import admin admin.autodiscover() urlpatterns = patterns(, # Example: # (r'^mysite/', include('mysite.foo.urls')), (r'^polls/$', 'mysite.polls.views.index'), (r'^polls/(?P<poll_id>\d+)/$', 'mysite.polls.views.detail'), (r'^polls/(?P<poll_id>\d+)/results/$', 'mysite.polls.views.results'), (r'^polls/(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'), # Uncomment the admin/doc line below and add 'django.contrib.admindocs' # to INSTALLED_APPS to enable admin documentation: # (r'^admin/doc/', include('dhttp://typea.info/tips/wiki.cgi?action=EDIT&page=Django+%BA%C7%BD%E9%A4%CE%A5%A2%A5%D7%A5%EA%A5%B1%A1%BC%A5%B7%A5%E7%A5%F3+3&artno=2jango.contrib.admindocs.urls')), # Uncomment the next line to enable the admin: (r'^admin/(.*)', admin.site.root), )
リクエストパラメータの解析
上記の例では、"/polls/23/"というリクエストがあった場合、DjangoはPythonモジュールをロードする。これは、r'^polls/(?P<poll_id>\d+)/$' にマッチするためであり、 mysite/polls/views.py.からdetail()関数が以下のように呼び出される。
detail(request=<HttpRequest object>, poll_id='23')
- poll_id='23' の部分は、(?P<poll_id>\d+)から来ている
- 丸括弧でパターンをを囲うことにより、テキストの一致を『捕捉』しビュー関数に引数として送信する。
- ?P<poll_id> がパターンにマッチした場合の名称を定義している。
- \d+ は正規表現で数字の連続
最初のビュー
ViewDoesNotExistエラー
- まだ、ビューを作成していないので、http://192.168.24.14:8080/polls/ にアクセスしても、以下のようにViewDoesNotExistエラーが表示される
- これは、index() 関数を mysite/polls/views.py. に実装していないため。
シンプルビューの実装
- mysite/polls/views.py に以下を記述してみる
from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the poll index.")
引数を取るビューの実装
- 引数をとる以下の関数を追加し、http://192.168.24.14:8080/polls/34/ にアクセス
def detail(request, poll_id): return HttpResponse("You're looking at poll %s." % poll_id)
URLに入力したパラメータを取得できている
実際になにかするビュー
簡単なビューの作成
- ビューは次の2つのうちどちらかを行う責任がある。
- リクエストされたページのコンテンツを含んだHttpResponseオブジェクトを返す
- Http404のようなエラーを返す
- 先ほどの例で、HttpResponseにメッセージを直接与えたように、Pollの最新の5件を出力するようにハードコーディングしてしまうと、ページの見た目を変えるためにPythonのコードに手を加えなければいけなくなってしまう。Pythonからデザインを分離するために、テンプレートを使用する。
- 以下のコードはpolls/index.htmlテンプレートをロードし、Contextというテンプレート変数名とPythonオブジェクトのディクショナリを引き渡す
from django.template import Context, loader from mysite.polls.models import Poll from django.http import HttpResponse def index(request): latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5] t = loader.get_template('polls/index.html') c = Context({ 'latest_poll_list': latest_poll_list }) return HttpResponse(t.render(c))
- ページをリロードするとテンプレートがまだないため、以下のようなエラーとなる
TemplateDoesNotExist at /polls/ polls/index.html
- 最初のアプリケーション 2でsetting.pyに指定したTEMPLATE_DIRS のディレクトリ以下に、pollsディレクトリを作成し、index.htmlファイルを作成する
- loader.get_template('polls/index.html') は、"[テンプレートディレクトリ]/polls/index.html"をファイルシステム上では指すようになる
index.html
{% if latest_poll_list %} <ul> {% for poll in latest_poll_list %} <li>テンプレート:Poll.question</li> {% endfor %} </ul> {% else %} <p>No polls are available.</p> {% endif %}
ショートカット : render_to_response()
- テンプレートをロードするのによく使われる方法は、コンテキストとHttpResponseオブジェクトをテンプレートとともに詰めて返す。
書き直してみる
from django.shortcuts import render_to_response from mysite.polls.models import Poll def index(request): latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5] return render_to_response('polls/index.html',{'latest_poll_list': latest_poll_list})
上と同じ結果が得られる
404エラー
Poll詳細ビューを作成してみる
from django.http import Http404from mysite.polls.models import Poll : def detail(request, poll_id): try: p = Poll.objects.get(pk=poll_id) except Poll.DoesNotExist: raise Http404 return render_to_response('polls/detail.html', { 'poll': p })
- Poll のIDが見つからない場合、HTTP 404エラーを返す
- detail.htmlにとりあえず、以下の内容を記述
テンプレート:Poll
- 存在するIDを指定
- 存在しないIDを指定
ショートカット : get_object_or_404()
- Djangoはこのような場合にもショートカットを提供
from django.shortcuts import render_to_response, get_object_or_404 from mysite.polls.models import Poll def detail(request, poll_id): p = get_object_or_404(Poll, pk=poll_id) return render_to_response('polls/detail.html', { 'poll': p })
- get_list_or_404()関数も、同様に動作する。get()の代わりに、filter()を利用することを除いて。リストが空の場合、404エラーを返す。
テンプレートシステムを利用する
- detail()ビューは、次のような感じ
<h1>テンプレート:Poll.question</h1> <ul> {% for choice in poll.choice_set.all %} <li>テンプレート:Choice.choice</li> {% endfor %} </ul>
- テンプレートでは、ドット記法で、変数の属性にアクセスできる。テンプレート:Poll.questionのように
- 詳しくはテンプレートガイド参照
URLconfs
簡単にする
- 各コールバック関数共通の、「mysite.polls.vews」をプレフィックスとして外だしできる
urlpatterns = patterns('mysite.polls.vews', (r'^polls/$', 'index'), (r'^polls/(?P<poll_id>\d+)/$', 'detail'), (r'^polls/(?P<poll_id>\d+)/results/$', 'results'), (r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'), )
分割する
- Pollアプリケーションの可搬性を高める
- 今までの、mysite/urls.py を mysite/polls/urls.py にコピー
mysite/urls.py
- mysite/polls/urls.pyを取り込む
urlpatterns = patterns(, (r'^polls/', include('mysite.polls.urls')), )
mysite/polls/urls.py
urlpatterns = patterns('mysite.polls.views', (r'^$', 'index'), (r'^(?P<poll_id>\d+)/$', 'detail'), (r'^(?P<poll_id>\d+)/results/$', 'results'), (r'^(?P<poll_id>\d+)/vote/$', 'vote'), )
[[Django 最初のアプリケーション 4]|[前|Django 最初のアプリケーション 2]]][[[次]]
© 2006 矢木浩人