高機能WebフレームワークDjangoの全貌を解き明かす
Djangoの中核:MVTアーキテクチャ
Djangoは、MVT(Model-View-Template)と呼ばれるアーキテクチャパターンを採用しています。これは、Web開発で広く知られるMVC(Model-View-Controller)パターンに似ていますが、Django独自のアレンジが加えられています。
MVTアーキテクチャは、アプリケーションの関心事を以下の3つのコンポーネントに分離します。
- Model(モデル):
- アプリケーションのデータ構造とビジネスロジックを定義します。
- データベースとのやり取りを担当します。Djangoの強力なORM(Object-Relational Mapper)を通じて、Pythonのクラスとしてデータベースのテーブルを表現し、SQLを直接書かずにデータベース操作(作成、読み取り、更新、削除 – CRUD)を可能にします。
- モデル定義に基づいて、データベースのマイグレーション(スキーマ変更)ファイルを自動生成できます。
- View(ビュー):
- ユーザーからのHTTPリクエストを受け取り、適切な処理を行います。
- 必要に応じてModelを通じてデータベースからデータを取得・操作します。
- 処理結果をTemplateに渡し、最終的なHTTPレスポンス(通常はHTML)を生成してユーザーに返します。
- アプリケーションの中心的なロジックが実装される場所です。
- Djangoでは、関数ベースビュー(FBV)とクラスベースビュー(CBV)の2つの形式でビューを記述できます。
- Template(テンプレート):
- ユーザーインターフェース(主にHTML)の構造を定義します。
- Viewから渡されたデータ(コンテキスト)を埋め込み、動的なWebページを生成します。
- Djangoテンプレート言語(DTL)という独自の言語を使用し、変数表示(
{{ variable }})、タグ({% tag %})、フィルター({{ variable|filter }})などを利用してロジックを記述できます。 - テンプレート継承(
{% extends %},{% block %})により、共通レイアウトの再利用を容易にします。
このMVTアーキテクチャにより、各コンポーネントが独立して開発・テスト・保守できるようになり、コードの整理、再利用性、保守性が向上します。特にチームでの開発において、役割分担がしやすくなるという大きな利点があります。
URLディスパッチャの役割
MVTのコンポーネントではありませんが、Djangoのリクエスト処理フローにおいて重要なのがURLディスパッチャ(URLconf)です。
- ユーザーがアクセスしたURLに基づいて、どのViewがそのリクエストを処理すべきかを決定します。
urls.pyファイルにURLパターンと対応するViewのマッピングを記述します。- 正規表現やパスコンバータを使用して、URLからパラメータを抽出し、Viewに渡すことができます。
処理の流れ(簡易版):
- ユーザーがブラウザから特定のURLにアクセス(HTTPリクエスト)。
- Djangoがリクエストを受け取る。
- URLディスパッチャがURLを解析し、対応するViewを特定する。
- 特定されたViewがリクエストを処理する。必要であればModelを介してデータベースと対話する。
- Viewが処理結果(データ)をTemplateに渡す。
- Templateがデータを受け取り、最終的なHTMLを生成する。
- Viewが生成されたHTMLを含むHTTPレスポンスをユーザーのブラウザに返す。
Djangoプロジェクトをはじめよう!
Djangoを使ったWebアプリケーション開発の第一歩は、プロジェクトのセットアップです。
1. Djangoのインストール
まず、Pythonのパッケージ管理ツールであるpipを使ってDjangoをインストールします。仮想環境を作成して、その中で作業することを強く推奨します。これにより、プロジェクトごとに依存関係を分離できます。
# 仮想環境を作成 (myenv は任意の名前)
python -m venv myenv
# 仮想環境を有効化 (Windowsの場合)
myenv\Scripts\activate
# 仮想環境を有効化 (macOS/Linuxの場合)
source myenv/bin/activate
# Djangoをインストール (特定のバージョンをインストールする場合)
# 例: Django 5.1.7 (2025年3月6日リリース)
python -m pip install Django==5.1.7
# 最新版をインストールする場合
python -m pip install django
# インストールされたか確認
python -m django --version2025年4月1日現在、Djangoの最新安定版は5.1系です。Djangoは定期的にフィーチャーリリース(約8ヶ月ごと)とパッチリリース(必要に応じて)が行われています。
2. プロジェクトの作成
Djangoがインストールされたら、django-adminコマンドを使って新しいプロジェクトを作成します。
# 'myproject' という名前のプロジェクトを作成
# 最後の '.' は、現在のディレクトリ直下にプロジェクトファイルを作成する指示
django-admin startproject myproject .これにより、以下のようなディレクトリ構造が生成されます。
myproject/ manage.py myproject/ __init__.py settings.py urls.py asgi.py wsgi.pymanage.py: プロジェクト管理用のコマンドラインユーティリティ。- 内側の
myproject/ディレクトリ: プロジェクトのPythonパッケージ。 settings.py: プロジェクト全体の設定ファイル。urls.py: プロジェクトのURL定義(URLディスパッチャ)。asgi.py,wsgi.py: Webサーバーとのインターフェース設定。
3. アプリケーションの作成
Djangoプロジェクトは、一つ以上の「アプリケーション」から構成されます。アプリケーションは、特定の機能(例:ブログ機能、ユーザー認証機能)を担当する自己完結型のモジュールです。manage.pyを使ってアプリケーションを作成します。
# 'polls' という名前のアプリケーションを作成
python manage.py startapp pollsこれにより、pollsディレクトリが作成され、その中にアプリケーション固有のファイル(models.py, views.py, admin.py, apps.pyなど)が生成されます。
polls/ __init__.py admin.py apps.py migrations/ __init__.py models.py tests.py views.py作成したアプリケーションは、プロジェクトの設定ファイル(myproject/settings.py)のINSTALLED_APPSリストに追加して、プロジェクトに認識させる必要があります。
# myproject/settings.py
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'polls', # 作成したアプリケーションを追加
]4. 開発サーバーの起動
Djangoには、開発用の軽量なWebサーバーが組み込まれています。以下のコマンドで起動できます。
python manage.py runserverサーバーが起動したら、Webブラウザで http://127.0.0.1:8000/ にアクセスすると、Djangoのデフォルトのウェルカムページが表示されるはずです。これで開発を始める準備が整いました!
モデル (Model) – データの定義
モデルはアプリケーションのデータの構造を定義する場所です。Djangoでは、Pythonのクラスとしてモデルを定義し、それぞれのクラスがデータベースのテーブルに、クラスの属性がテーブルのカラムに対応します。
モデルは通常、アプリケーションディレクトリ内のmodels.pyファイルに記述します。
# polls/models.py
from django.db import models
import datetime
from django.utils import timezone
class Question(models.Model): question_text = models.CharField(max_length=200, verbose_name='質問文') pub_date = models.DateTimeField(verbose_name='公開日') def __str__(self): return self.question_text def was_published_recently(self): now = timezone.now() return now - datetime.timedelta(days=1) <= self.pub_date <= now was_published_recently.admin_order_field = 'pub_date' was_published_recently.boolean = True was_published_recently.short_description = '最近公開されたか?'
class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE, verbose_name='質問') choice_text = models.CharField(max_length=200, verbose_name='選択肢テキスト') votes = models.IntegerField(default=0, verbose_name='投票数') def __str__(self): return self.choice_text上記の例では、QuestionとChoiceという2つのモデルを定義しています。
- 各クラスは
django.db.models.Modelを継承します。 - クラス属性としてフィールドを定義します。
CharField(文字列)、DateTimeField(日時)、IntegerField(整数)、ForeignKey(外部キー、リレーションシップ)など、様々なフィールドタイプが用意されています。 verbose_name引数で、管理画面などで表示されるフィールドの人間可読な名前を指定できます。ForeignKeyは多対一のリレーションシップを定義します。on_delete=models.CASCADEは、参照先のQuestionが削除された場合に、関連するChoiceも一緒に削除されることを意味します。__str__()メソッドは、オブジェクトを文字列として表現する際に使われます(主に管理画面やデバッグ時)。- モデルにはカスタムメソッド(例:
was_published_recently)を追加することもできます。
マイグレーション
モデル定義を変更(フィールドの追加、削除、変更など)した場合、その変更をデータベーススキーマに反映させる必要があります。このプロセスをマイグレーションと呼びます。
- マイグレーションファイルの作成: モデルの変更点を検出し、マイグレーションファイル(Pythonスクリプト)を生成します。
(python manage.py makemigrations pollspollsはアプリケーション名) - マイグレーションの適用: 生成されたマイグレーションファイルに基づいて、データベーススキーマを更新します。
python manage.py migrate
Djangoのマイグレーションシステムにより、データベーススキーマの変更履歴を管理し、チーム開発やデプロイ時のスキーマ更新を安全かつ簡単に行うことができます。
ビュー (View) – リクエストの処理
ビューは、ユーザーからのリクエストを受け取り、ビジネスロジックを実行し、レスポンスを返す役割を担います。データベースからデータを取得したり、フォームからの入力を処理したり、適切なテンプレートを選択してレンダリングしたりします。
ビューは通常、アプリケーションディレクトリ内のviews.pyファイルに記述します。
関数ベースビュー (Function-Based Views – FBV)
最もシンプルな形式で、Pythonの関数としてビューを定義します。関数はHttpRequestオブジェクトを引数として受け取り、HttpResponseオブジェクトを返す必要があります。
# polls/views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.urls import reverse
from .models import Question, Choice
def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] context = {'latest_question_list': latest_question_list} # render() は HttpRequest, テンプレート名, コンテキスト辞書を引数にとる return render(request, 'polls/index.html', context)
def detail(request, question_id): # get_object_or_404() はオブジェクトが存在しない場合にHttp404例外を送出するショートカット question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/detail.html', {'question': question})
def results(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/results.html', {'question': question})
def vote(request, question_id): question = get_object_or_404(Question, pk=question_id) try: # request.POST は POSTデータを辞書ライクにアクセスできるオブジェクト selected_choice = question.choice_set.get(pk=request.POST['choice']) except (KeyError, Choice.DoesNotExist): # エラーメッセージ付きで質問詳細フォームを再表示 return render(request, 'polls/detail.html', { 'question': question, 'error_message': "選択肢を選んでいません。", }) else: selected_choice.votes += 1 selected_choice.save() # POSTデータの処理成功後は、常にHttpResponseRedirectを返すのが良いプラクティス # これにより、ユーザーがブラウザの「戻る」ボタンを押してもデータが二重に送信されるのを防ぐ # reverse() はビュー名と引数からURLを生成するヘルパー関数 return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))render(): テンプレートをレンダリングし、HttpResponseを返すショートカット関数。get_object_or_404(): 指定された条件でオブジェクトを取得しようとし、見つからなければ404エラーを発生させるショートカット関数。request.POST: POSTリクエストで送信されたデータにアクセスするための辞書風オブジェクト。HttpResponseRedirect: ユーザーを指定したURLにリダイレクトさせるためのレスポンス。reverse(): URLパターン名から実際のURLパスを生成する関数。ハードコーディングを避けられます。
クラスベースビュー (Class-Based Views – CBV)
より構造化され、再利用性の高いビューを記述するために、クラスとしてビューを定義することもできます。Djangoには、一般的なビューのパターン(リスト表示、詳細表示、作成、更新、削除など)を実装するための汎用クラスベースビューが多数用意されています。
# polls/views.py (クラスベースビュー版)
from django.views import generic
from django.utils import timezone
class IndexView(generic.ListView): template_name = 'polls/index.html' context_object_name = 'latest_question_list' # テンプレートで使う変数名 def get_queryset(self): """最近公開された5件の質問を返す""" return Question.objects.filter( pub_date__lte=timezone.now() # 公開日が現在以前 ).order_by('-pub_date')[:5]
class DetailView(generic.DetailView): model = Question # 対象モデル template_name = 'polls/detail.html' def get_queryset(self): """ まだ公開されていない質問は除外する """ return Question.objects.filter(pub_date__lte=timezone.now())
class ResultsView(generic.DetailView): model = Question template_name = 'polls/results.html'
# vote関数はロジックが少し複雑なので、関数ベースビューのままが適切な場合もある
# (上記FBVのvote関数を参照)クラスベースビューを使うと、定型的な処理をDjangoに任せることができ、コード量を削減できます。特にListViewやDetailViewなどの汎用ビューは非常によく使われます。
テンプレート (Template) – 表示の生成
テンプレートは、最終的にユーザーに表示されるHTMLの構造を定義します。Djangoテンプレート言語(DTL)を使い、ビューから渡されたデータを埋め込んだり、簡単なロジックを実行したりできます。
テンプレートファイルは通常、アプリケーションディレクトリ内のtemplates/アプリケーション名/ディレクトリ(例: polls/templates/polls/)に配置します。
基本的な構文
- 変数 (Variables):
{{ variable_name }}ビューから渡されたコンテキスト辞書内の変数の値を表示します。ドット(
.)を使って属性や辞書のキー、リストのインデックスにアクセスできます(例:{{ question.question_text }},{{ my_dict.key }},{{ my_list.0 }})。 - タグ (Tags):
{% tag_name %} ... {% endtag_name %}テンプレートのロジック(制御フロー)を担当します。一般的なタグには以下のようなものがあります。
{% for item in list %} ... {% endfor %}: ループ処理{% if condition %} ... {% elif another_condition %} ... {% else %} ... {% endif %}: 条件分岐{% url 'url_name' arg1 arg2 %}: URLパターン名からURLを生成{% load static %}: 静的ファイル(CSS, JS, 画像)へのパスを生成するために必要{% extends 'base.html' %}: テンプレート継承の親テンプレートを指定{% block content %} ... {% endblock %}: 継承先でオーバーライドされるブロックを定義
- フィルター (Filters):
{{ variable|filter_name:argument }}変数の表示形式を変更します。パイプ(
|)で変数とフィルター名を繋ぎます。{{ value|lower }}: 小文字に変換{{ value|date:"Y/m/d" }}: 日付を指定形式でフォーマット{{ value|length }}: リストや文字列の長さを取得{{ value|linebreaksbr }}: 改行を<br>タグに変換
- コメント (Comments):
{# comment text #}テンプレート内にコメントを記述します。複数行コメントは
{% comment %} ... {% endcomment %}を使います。
テンプレートの例 (polls/templates/polls/index.html)
<!-- polls/templates/polls/base.html を継承 -->
{% extends 'polls/base.html' %}
{% block title %}投票アプリ - ホーム{% endblock %}
{% block content %} <h1 class="title">利用可能な投票</h1> {% if latest_question_list %} <ul> {% for question in latest_question_list %} <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li> {% endfor %} </ul> {% else %} <p class="notification is-warning">利用可能な投票はありません。</p> {% endif %}
{% endblock %}テンプレート継承
テンプレート継承は、サイトの共通レイアウト(ヘッダー、フッター、サイドバーなど)をbase.htmlのような親テンプレートに定義し、各ページの固有コンテンツ部分だけを子テンプレートで記述する強力な機能です。
- 親テンプレート (
base.html) で{% block block_name %} ... {% endblock %}を使って、子テンプレートが内容を挿入できる「ブロック」を定義します。 - 子テンプレートの先頭で
{% extends 'base.html' %}を記述し、親テンプレートを指定します。 - 子テンプレート内で
{% block block_name %} ... {% endblock %}を使って、親テンプレートの対応するブロックの内容を上書きします。
これにより、HTMLの重複を避け、サイト全体のデザイン変更などを容易に行えます。
URLルーティング (URLconf)
URLルーティングは、特定のURLパスをどのビュー関数またはクラスにマッピングするかを定義する仕組みです。Djangoでは、プロジェクトのurls.pyと、各アプリケーションのurls.pyを使ってURL設定(URLconf)を構築します。
プロジェクトのurls.py
プロジェクト全体のURL設定のエントリーポイントです。通常、アプリケーションごとのURL設定をinclude()関数を使って読み込みます。
# myproject/urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [ # '/admin/' へのアクセスはDjango管理サイトへ path('admin/', admin.site.urls), # '/polls/' で始まるURLは polls.urls モジュールで処理 path('polls/', include('polls.urls')), # 他のアプリケーションのURL設定もここに追加 # path('blog/', include('blog.urls')),
]アプリケーションのurls.py
各アプリケーション固有のURLパターンとビューのマッピングを定義します。
# polls/urls.py
from django.urls import path
from . import views # polls/views.py をインポート
# アプリケーションの名前空間を設定 (テンプレートでの {% url %} タグで利用)
app_name = 'polls'
urlpatterns = [ # 例: /polls/ path('', views.IndexView.as_view(), name='index'), # 例: /polls/5/ # <int:question_id> は整数型のパスコンバータ。URLの一部をキャプチャし、ビューに関数引数として渡す path('<int:question_id>/', views.DetailView.as_view(), name='detail'), # 例: /polls/5/results/ path('<int:question_id>/results/', views.ResultsView.as_view(), name='results'), # 例: /polls/5/vote/ path('<int:question_id>/vote/', views.vote, name='vote'),
]</int:question_id>path()関数は、URLパターン、対応するビュー、オプションのname(URLパターンに名前を付ける)を引数に取ります。- パスコンバータ(例:
<int:pk>,<str:username>,<slug:slug>)を使うと、URLの一部を動的にキャプチャし、ビューにキーワード引数として渡すことができます。 name引数でURLパターンに名前を付けておくと、テンプレートの{% url %}タグやビュー内のreverse()関数で、URLをハードコーディングせずに参照できるため、URL構造の変更に強くなります。app_nameを設定することで、複数のアプリケーションで同じURL名が衝突するのを防ぐ名前空間を作成できます(例:{% url 'polls:detail' question.id %})。
このようにURL設定を分離することで、アプリケーションの再利用性が高まり、プロジェクト全体のURL構造を管理しやすくなります。
Django ORM (Object-Relational Mapper)
Django ORMは、Pythonオブジェクトとリレーショナルデータベースの間をマッピングする強力な機能です。SQLを直接記述することなく、Pythonコードを使ってデータベースの操作(クエリ、作成、更新、削除)を行うことができます。
各モデルクラスには、データベースクエリのインターフェースとなるマネージャー (Manager) がデフォルトでobjectsという名前で追加されます。このマネージャーを通じてクエリを実行します。
基本的なクエリ操作
対話型シェル (python manage.py shell) を使うと、ORMの動作を簡単に試すことができます。
# Djangoシェルを起動
# python manage.py shell
from polls.models import Question, Choice
from django.utils import timezone
# --- Read (取得) ---
# 全てのQuestionオブジェクトを取得 (QuerySetを返す)
all_questions = Question.objects.all()
print(all_questions)
# <QuerySet [<Question: 最初の質問>, <Question: 次の質問>]>
# 特定の条件でフィルタリング (QuerySetを返す)
# pub_date が現在時刻以前のものをフィルタ
active_questions = Question.objects.filter(pub_date__lte=timezone.now())
print(active_questions)
# 特定のフィールドの値でフィルタ (完全一致)
q = Question.objects.filter(question_text="最初の質問")
print(q)
# __startswith, __endswith, __contains などで部分一致検索
q_start = Question.objects.filter(question_text__startswith="最初")
print(q_start)
# 主キー(pk)で単一のオブジェクトを取得 (オブジェクトを直接返す、存在しない場合はDoesNotExist例外)
try: q_pk1 = Question.objects.get(pk=1) print(q_pk1)
except Question.DoesNotExist: print("ID=1 の質問は見つかりません")
# 複数の条件 (AND)
q_complex = Question.objects.filter(pk=1, pub_date__year=2024)
# 複数の条件 (OR) - Qオブジェクトを使う
from django.db.models import Q
q_or = Question.objects.filter(Q(pk=1) | Q(question_text__contains="次"))
# exclude() で条件に一致しないものを取得
q_exclude = Question.objects.exclude(pub_date__year=2023)
# order_by() でソート
q_sorted = Question.objects.order_by('-pub_date') # '-' は降順
# スライシングで件数を制限
q_limited = Question.objects.all()[:5] # 最初の5件
# --- Create (作成) ---
# 方法1: create() メソッド
new_q = Question.objects.create( question_text="新しい質問?", pub_date=timezone.now()
)
print(new_q.id) # 作成されたオブジェクトのIDが取得できる
# 方法2: インスタンスを作成してsave()
q_instance = Question(question_text="インスタンスからの質問", pub_date=timezone.now())
q_instance.save() # この時点でデータベースに保存される
print(q_instance.id)
# --- Update (更新) ---
# 方法1: オブジェクトを取得して属性を変更し、save()
q_to_update = Question.objects.get(pk=1)
q_to_update.question_text = "更新された最初の質問"
q_to_update.save()
# 方法2: QuerySetに対してupdate() (複数オブジェクトを一括更新可能)
# 注意: この方法ではモデルのsave()メソッドは呼ばれない
Question.objects.filter(pub_date__year=2024).update(question_text="2024年の質問は更新")
# --- Delete (削除) ---
# 方法1: オブジェクトを取得してdelete()
q_to_delete = Question.objects.get(pk=2)
q_to_delete.delete()
# 方法2: QuerySetに対してdelete() (複数オブジェクトを一括削除可能)
# 注意: この方法ではモデルのdelete()メソッドは呼ばれない
Question.objects.filter(question_text__contains="古い").delete()
# --- リレーションシップの操作 ---
# Questionオブジェクト q から関連するChoiceを取得 (逆参照)
q = Question.objects.get(pk=1)
choices_for_q = q.choice_set.all() # デフォルトでは 'モデル名_set' でアクセス
print(choices_for_q)
# Choiceオブジェクトを作成し、Questionに関連付ける
q = Question.objects.get(pk=1)
new_choice = q.choice_set.create(choice_text="新しい選択肢", votes=0)
# または
choice_instance = Choice(question=q, choice_text="別の選択肢", votes=0)
choice_instance.save()ORMの重要な特徴は遅延評価 (Lazy Evaluation)です。filter()やexclude()、order_by()などのメソッドは、実際にはデータベースにアクセスせず、QuerySetオブジェクトを返すだけです。データベースへのアクセスは、QuerySetが評価されるタイミング(例: イテレーション時、スライス時、get()呼び出し時、list()変換時など)で初めて実行されます。これにより、効率的なクエリの構築が可能になります。
また、ORMはデータベースの種類(PostgreSQL, MySQL, SQLite, Oracleなど)の違いを抽象化してくれるため、データベースエンジンを変更する際も、アプリケーションコードの変更を最小限に抑えることができます。
注意点: N+1問題
ORMを使う上で注意が必要なのが「N+1問題」です。これは、リスト表示などで親オブジェクトを取得した後、各親オブジェクトに関連する子オブジェクトを取得するために、追加のクエリがN回(親オブジェクトの数だけ)発行されてしまう問題です。パフォーマンスのボトルネックになりやすいため、select_related(ForeignKey, OneToOneField用)やprefetch_related(ManyToManyField, 逆ForeignKey用)を使って、関連オブジェクトを効率的に一括取得するテクニックが重要になります。
Django管理サイト (Admin Site)
Djangoの強力な機能の一つが、自動生成される管理サイト(Admin Site)です。モデルを定義し、簡単な設定を行うだけで、データの作成・表示・更新・削除(CRUD)が可能な高機能な管理インターフェースを利用できます。
管理サイトの有効化とアクセス
django.contrib.adminの確認:settings.pyのINSTALLED_APPSに'django.contrib.admin'が含まれていることを確認します(通常デフォルトで含まれています)。- URL設定: プロジェクトの
urls.pyに管理サイトへのパスが含まれていることを確認します(通常デフォルトでpath('admin/', admin.site.urls),が含まれています)。 - データベースマイグレーション:
python manage.py migrateを実行して、管理サイトが必要とするデータベーステーブルを作成します。 - スーパーユーザーの作成: 以下のコマンドで、管理サイトにログインするための管理者アカウントを作成します。
ユーザー名、メールアドレス、パスワードの入力を求められます。python manage.py createsuperuser - アクセス: 開発サーバーを起動 (
python manage.py runserver) し、ブラウザでhttp://127.0.0.1:8000/admin/にアクセスします。作成したスーパーユーザーの認証情報でログインします。
モデルを管理サイトに登録
デフォルトでは、定義したモデルは管理サイトに表示されません。表示するには、各アプリケーションのadmin.pyファイルでモデルを登録する必要があります。
# polls/admin.py
from django.contrib import admin
from .models import Question, Choice # 管理したいモデルをインポート
# 最も簡単な登録方法
admin.site.register(Question)
admin.site.register(Choice)これで、管理サイトにログインすると「Polls」セクションが表示され、「Questions」と「Choices」を管理できるようになります。
管理サイトのカスタマイズ
管理サイトの表示や動作は、ModelAdminクラスを使って細かくカスタマイズできます。
# polls/admin.py
from django.contrib import admin
from .models import Question, Choice
# ChoiceをQuestionの編集ページ内でインライン表示するための設定
class ChoiceInline(admin.TabularInline): # または admin.StackedInline model = Choice extra = 3 # デフォルトで表示する空の選択肢フォームの数
class QuestionAdmin(admin.ModelAdmin): # 詳細編集ページのフィールド表示設定 # fields = ['pub_date', 'question_text'] # フィールドの表示順序を指定 fieldsets = [ (None, {'fields': ['question_text']}), ('日付情報', {'fields': ['pub_date'], 'classes': ['collapse']}), # フィールドセットでグループ化、折りたたみ可能に ] inlines = [ChoiceInline] # QuestionページにChoiceをインライン表示 # 一覧ページの表示設定 list_display = ('question_text', 'pub_date', 'was_published_recently') # 一覧に表示するフィールド list_filter = ['pub_date'] # 右サイドバーに表示されるフィルタ search_fields = ['question_text'] # 上部に検索ボックスを表示
# ModelAdminクラスを使ってモデルを登録
admin.site.register(Question, QuestionAdmin)
# ChoiceはQuestionAdminでインライン表示するので、単独での登録は不要な場合もある
# admin.site.register(Choice)ModelAdminクラスでは、以下のようなカスタマイズが可能です。
- 一覧ページ (Change List):
list_display: 一覧に表示するフィールドを指定。モデルのメソッドも指定可能。list_filter: 日付階層やフィールド値で絞り込むためのフィルターを追加。search_fields: 指定したフィールドを対象とする検索ボックスを追加。list_per_page: 1ページあたりに表示するアイテム数を指定。
- 編集/追加ページ (Change Form):
fields: 表示するフィールドとその順序を指定。fieldsets: フィールドをグループ化して表示。HTMLクラス(例:'collapse')で見た目を変更可能。exclude: 表示しないフィールドを指定。readonly_fields: 読み取り専用にするフィールドを指定。inlines: 関連するモデル(ForeignKeyやManyToManyField)を同じページ内で編集できるようにする(TabularInlineまたはStackedInlineを使用)。
- その他、アクションの追加、テンプレートのオーバーライドなど、高度なカスタマイズも可能です。
Django管理サイトは、アプリケーションの内部データを素早く確認・操作するための強力なツールであり、開発初期段階やコンテンツ管理において非常に役立ちます。
フォーム (Forms) – ユーザー入力の処理
Webアプリケーションでは、ユーザーからのデータ入力(検索クエリ、登録情報、投稿内容など)を受け付けるフォームが不可欠です。Djangoのフォームライブラリは、HTMLフォームのレンダリング、データのバリデーション(検証)、データのクリーニング(整形)を簡単に行うための仕組みを提供します。
フォームクラスの定義
フォームはPythonのクラスとして定義します。通常、アプリケーションディレクトリ内にforms.pyというファイルを作成して記述します。
# myapp/forms.py (例)
from django import forms
class ContactForm(forms.Form): name = forms.CharField(max_length=100, label='お名前', required=True) email = forms.EmailField(label='メールアドレス', required=True) message = forms.CharField(widget=forms.Textarea, label='お問い合わせ内容', required=True) send_copy = forms.BooleanField(required=False, initial=False, label='自分にもコピーを送る')- フォームクラスは
django.forms.Formを継承します。 - クラス属性としてフォームフィールドを定義します。
CharField,EmailField,IntegerField,BooleanField,ChoiceField,DateFieldなど、様々なフィールドタイプがあります。 - 各フィールドには、
label(表示ラベル)、required(必須かどうか)、initial(初期値)、widget(HTMLウィジェットの指定、例:Textarea,PasswordInput,Select)などのオプションを指定できます。 max_length,min_lengthなどのバリデーションルールもフィールド定義時に指定できます。
ビューでのフォーム処理
ビュー関数またはクラスでフォームクラスをインスタンス化し、リクエストに応じて処理を行います。
# myapp/views.py
from django.shortcuts import render, redirect
from django.http import HttpResponseRedirect
from .forms import ContactForm # 作成したフォームクラスをインポート
from django.core.mail import send_mail
def contact_view(request): if request.method == 'POST': # POSTリクエストの場合、送信されたデータでフォームを初期化 form = ContactForm(request.POST) if form.is_valid(): # バリデーションが成功した場合 name = form.cleaned_data['name'] email = form.cleaned_data['email'] message = form.cleaned_data['message'] send_copy = form.cleaned_data['send_copy'] # ここでメール送信などの処理を実行 subject = f'お問い合わせがありました: {name} 様より' body = f'メールアドレス: {email}\n\n内容:\n{message}' from_email = 'noreply@example.com' # 送信元メールアドレス recipient_list = ['admin@example.com'] # 送信先リスト if send_copy: recipient_list.append(email) try: send_mail(subject, body, from_email, recipient_list) # 成功したらサンクスページなどにリダイレクト return redirect('myapp:contact_success') # 'contact_success' はURL名 except Exception as e: # エラー処理 print(f"メール送信エラー: {e}") # エラーメッセージをフォームに追加することも可能 form.add_error(None, "メールの送信中にエラーが発生しました。") else: # GETリクエストの場合、空のフォームを初期化 form = ContactForm() # フォームをテンプレートに渡してレンダリング return render(request, 'myapp/contact_form.html', {'form': form})
def contact_success_view(request): return render(request, 'myapp/contact_success.html')- GETリクエストの場合は、空のフォーム (
form = ContactForm()) を作成してテンプレートに渡します。 - POSTリクエストの場合は、送信されたデータ (
request.POST) を使ってフォームを初期化 (form = ContactForm(request.POST)) します。 form.is_valid()メソッドを呼び出してバリデーションを実行します。- バリデーションが成功すると
Trueを返し、クリーニングされたデータがform.cleaned_data辞書に格納されます。 - バリデーションが失敗すると
Falseを返し、エラー情報がフォームオブジェクトに追加されます。このフォームをテンプレートで再レンダリングすると、エラーメッセージが表示されます。
- バリデーションが成功すると
form.cleaned_dataから安全にデータを取り出し、メール送信などの処理を行います。- 処理成功後は、通常
HttpResponseRedirectやredirect()を使って別のページ(例: サンクスページ)にリダイレクトします。
テンプレートでのフォームレンダリング
ビューから渡されたフォームオブジェクトをテンプレートでレンダリングします。DjangoはフォームフィールドをHTMLとして表示する簡単な方法を提供します。
<!-- myapp/templates/myapp/contact_form.html -->
{% extends 'myapp/base.html' %}
{% block content %} <h1 class="title">お問い合わせ</h1> <form method="post" novalidate> {% csrf_token %} <!-- CSRF対策のため必須 --> <!-- フォーム全体のエラー表示 --> {% if form.non_field_errors %} <div class="notification is-danger"> {{ form.non_field_errors }} </div> {% endif %} <!-- 各フィールドのレンダリング --> <!-- 方法1: as_p (各要素を<p>タグで囲む) --> {# {{ form.as_p }} #} <!-- 方法2: as_ul (各要素を<li>タグで囲む) --> {# {{ form.as_ul }} #} <!-- 方法3: as_table (各要素を<tr><th><td>タグで囲む) --> {# <table> {{ form.as_table }} </table> #} <!-- 方法4: フィールドごとに手動でレンダリング (Bulmaスタイル適用例) --> {% for field in form %} <div class="field"> <label class="label" for="{{ field.id_for_label }}">{{ field.label }}</label> <div class="control"> {{ field }} <!-- フィールド本体のウィジェット --> </div> {% if field.help_text %} <p class="help">{{ field.help_text }}</p> {% endif %} {% if field.errors %} <p class="help is-danger">{{ field.errors }}</p> <!-- フィールド固有のエラー --> {% endif %} </div> {% endfor %} <div class="field"> <div class="control"> <button type="submit" class="button is-link">送信</button> </div> </div> </form>
{% endblock %}{% csrf_token %}はクロスサイトリクエストフォージェリ(CSRF)攻撃を防ぐために必須です。{{ form.as_p }},{{ form.as_ul }},{{ form.as_table }}は、フォーム全体を特定のHTML構造で簡単にレンダリングする方法です。- フィールドを個別にレンダリング (
{{ form.field_name }}) することで、HTML構造やCSSクラスをより細かく制御できます。ループ ({% for field in form %}) を使うと便利です。 {{ field.label }},{{ field.id_for_label }},{{ field.help_text }},{{ field.errors }}などを利用して、ラベル、ヘルプテキスト、エラーメッセージを表示できます。
ModelForm
モデルオブジェクトの作成や更新を行うフォームを定義する場合、ModelFormを使うと非常に便利です。ModelFormは、指定したモデルに基づいて自動的にフォームフィールドを生成してくれます。
# polls/forms.py (ModelFormの例)
from django import forms
from .models import Question # Questionモデルをインポート
class QuestionForm(forms.ModelForm): class Meta: model = Question # 対象のモデルを指定 fields = ['question_text', 'pub_date'] # フォームに含めるフィールドを指定 # exclude = ['field_to_exclude'] # 除外するフィールドを指定することも可能 widgets = { 'pub_date': forms.DateTimeInput(attrs={'type': 'datetime-local'}), } labels = { 'question_text': '質問の内容', 'pub_date': '公開日時', } help_texts = { 'pub_date': '未来の日付も設定可能です。', }ビューでの処理も似ていますが、form.save()メソッドを呼び出すだけで、フォームのデータに基づいてモデルオブジェクトを作成または更新できます。
Djangoのフォームシステムは、定型的なフォーム処理を大幅に簡略化し、バリデーションロジックを一箇所にまとめ、セキュリティ(CSRF対策など)も考慮されているため、安全で効率的な開発を支援します。
その他の重要な機能
Djangoには、ここまで解説してきた中核機能以外にも、Webアプリケーション開発を支援する多くの便利な機能が組み込まれています。
- 静的ファイル管理 (Static Files): CSS、JavaScript、画像などの静的ファイルを効率的に管理・配信する仕組み。
settings.pyでのSTATIC_URL,STATICFILES_DIRS,STATIC_ROOTの設定と、{% load static %}テンプレートタグ、collectstatic管理コマンドなどが中心。 - ユーザー認証システム (Authentication System): ユーザーアカウント、グループ、パーミッション、クッキーベースのセッション管理など、堅牢な認証・認可機能を提供。
django.contrib.authに含まれるモデルやビュー、フォームを利用。 - ミドルウェア (Middleware): リクエストとレスポンスの処理パイプラインにフックを挿入する仕組み。セッション管理、CSRF保護、認証などがミドルウェアとして実装されている。カスタムミドルウェアを作成して、リクエスト/レスポンス処理に独自のロジックを追加可能。
- シグナル (Signals): アプリケーション内で特定のイベント(例: モデルの保存前/後、ユーザーログイン時)が発生した際に、他の箇所に通知を送り、アクションを実行させるための仕組み(デカップリング)。
- キャッシュフレームワーク (Caching Framework): 動的コンテンツの結果をキャッシュし、パフォーマンスを向上させるための仕組み。メモリ、データベース、ファイルシステムなど、様々なキャッシュバックエンドをサポート。
- テストフレームワーク (Testing Framework): ユニットテスト、インテグレーションテストを作成・実行するためのツールを提供。
unittestやpytestと連携可能。 - 国際化と地域化 (Internationalization and Localization): アプリケーションを多言語対応させたり、地域ごとの日付・数値フォーマットに対応させたりするための機能。
- セキュリティ機能: CSRF保護、クリックジャッキング保護、SQLインジェクション対策(ORMによる)、XSS(クロスサイトスクリプティング)対策(テンプレートの自動エスケープ)など、多くのセキュリティ対策がデフォルトで組み込まれている。
- 地理空間フレームワーク (GeoDjango): 地理空間データ(GIS)を扱うための拡張機能。地理的なクエリや空間データベースをサポート。
- Django REST framework (DRF): (サードパーティライブラリだが非常に一般的) RESTful APIを構築するための強力なツールキット。シリアライゼーション、認証、パーミッション、ビューセット、ルーティングなどを提供。
これらの「バッテリー同梱」の機能により、開発者は基本的ながらも重要な多くの要素をゼロから構築する必要がなく、アプリケーション固有の価値創造に集中することができます。