Django(ジャンゴ)は、Pythonで作られたWebフレームワークです。
PythonはAIのライブラリが充実しており、近年非常に注目されている言語です。そして、Pythonを使ってWebシステムを作る際に便利なのがDjangoです。
この記事は開発環境の構築から始まり、実際にシステムが動くまでをサンプルコードと共に紹介します。
- 間違っている可能性もあるので自己責任で参考にしてください。
- Pythonの基本的な部分などは省略しています。
Pythonのインストール
DjangoはPythonで実装されているため、まずは開発端末にPythonをインストールします。
Pythonの公式サイトからご自身の環境に合ったPythonのダウンロードを行い、ウィザードに従ってインストールしてください。
https://www.python.org/downloads/
インストールする際は「Add Python 〇〇 to PATH」にチェックを入れて、環境変数のPATHにPythonを追加します。
Djangoのプロジェクトを作成
まずは、Djangoのプロジェクトを格納するフォルダを作成します。ここではプロジェクト名をdjangotestとしていますが、好きな名前にしてください。
mkdir djangotest
cd djangotest
次に、pipというPythonのパッケージ管理ツールを利用して、pipenvをインストールします。
pip install pipenv
pipはPythonをインストールした際に含まれていると思いますが、古いバージョンのPythonなどは含まれていない可能性があります。その場合は個別にpipをインストールしてください。
Djangoやその他のパッケージをインストールする際は、仮想環境を作ってそこにインストールすることが一般的です。仮想環境を作成するにはvenvコマンドを使い、パッケージのインストールにはpipコマンドを使います。
そして、venvとpipを一つにまとめたものがpipenvです。とても便利なので、この記事ではpipenvを使っていきます。
pipenvをインストールしたら、以下のコマンドで仮想環境を作成します。pythonのバージョンは変更してください。
pipenv --python 3.9
このコマンドにより、Pipfileが作成されます。Pipfileはnpmのpackage.jsonと似たようなもので、インストールしたパッケージの一覧が記述されており、これを使ってパッケージの一括インストールが可能です。
仮想環境にパッケージをインストールするため、以下のコマンドで仮想環境に入ります。
pipenv shell
仮想環境に入ったら、各種パッケージをインストールします。ここではDjangoのみをインストールしていますが、必要な機能に応じて追加でインストールしてください。
pipenv install django
Djangoのプロジェクトを作成します。最後のドットがないとconfigフォルダが二重に作成されるので注意してください。
django-admin startproject config .
Djangoのアプリケーションを作成します。Djangoは一つのプロジェクトの中に複数のアプリケーションを構築できることが重要なポイントです。アプリケーション名となるdjangoappの部分は自由に変えてください。
python manage.py startapp djangoapp
以上でDjangoのプロジェクトを作成できました。最後に、以下のコマンドでDjangoの開発サーバーを立ち上げてみます。
python manage.py runserver
正常に開発環境が構築できていれば、ブラウザで「http://127.0.0.1:8000/」にアクセスすると、Djangoの初期画面が表示されます。開発サーバーの停止は「ctrl+c」です。
今後開発を進めていく際、コードを変更すると開発サーバーが自動でリロードされるため、コードの変更を即座にブラウザ上で確認できます。ただし、一部の変更は反映されないため、その場合は手動で再起動する必要があります。
設定ファイルの修正
configフォルダの中にsettings.pyという設定ファイルがあります。Visual Studio Codeなどのエディターで開き、修正していきます。
言語とタイムゾーンの設定です。
LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'
INSTALLED_APPSには、デフォルトで認証システムやセッションフレームワークなどのアプリケーションが有効になっています。ここに先ほど作成したアプリケーションの構成クラスを追記することで、Djangoが認識できます。
追記する内容は、Djangoのアプリケーションフォルダ(ここではdjangoapp)の中のapps.pyに記述されているclass名です。
INSTALLED_APPS = [
'djangoapp.apps.DjangoappConfig',
# 省略
]
その他にも、データベースをデフォルトのSQLiteから変更したり、外部ファイルに環境変数を定義したりと、必要に応じてsettings.pyを修正してください。
プロジェクトのURLディスパッチャを修正
urls.pyには、URLに応じて呼ばれるメソッド(ページ)を定義します。
# 省略
from django.urls import path, include
urlpatterns = [
# 省略
path("djangoapp/", include("djangoapp.urls")),
]
上記はアプリケーションフォルダにあるurls.pyをincludeで呼び出しています。つまり、「djangoapp」というURLでアクセスしたときは、アプリケーションのurls.pyでルーティングを行います。
プロジェクトのurls.pyにはアプリケーションのurls.pyを呼び出す処理を書き、実際のルーティングはアプリケーションのurls.pyに書くことで、保守性が向上します。
初期状態ではアプリケーションフォルダにurls.pyは存在しないので、後述する手順で新たに作成する必要があります。
上記の場合、アプリケーションのトップのURLは「http://127.0.0.1:8000/djangoapp/」という感じになります。「http://127.0.0.1:8000/」というURLに変更したい場合は以下のように記述します。
urlpatterns = [
# 省略
path("", include("djangoapp.urls")),
]
テンプレートの作成
テンプレートは、画面に表示されるHTMLのことです。
どこにテンプレートを作成するかについては、DjangoはデフォルトでINSTALLED_APPSの「templates」ディレクトリからテンプレートを探します。
アプリケーションフォルダの中に「templates」フォルダを作り、さらにその中に「djangoapp(アプリケーション名)」フォルダを作ります。そして、そこにindex.htmlなどのファイルを追加していきます。
つまり、「djangoapp/templates/djangoapp/index.html」のような階層になります。
templatesの後のdiangoappは不要に見えますが、これはテンプレートの名前空間として機能します。複数のアプリケーションが存在する場合、名前空間がないと同名のテンプレートを区別できません。
Djangoは親のテンプレートを作成して継承できます。
例えば、ヘッダーやフッターなどはページ全体で共通なケースがあります。そのような場合は、ヘッダーやフッターなどを記述した親テンプレートを作成します。そして、子のテンプレートはそれを継承して、コンテンツ部分だけを記述します。
以下は親テンプレートのサンプルです。
{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title></title>
<link
rel="stylesheet"
type="text/css"
href="{% static 'djangoapp/css/style.min.css' %}"
/>
<link rel="icon" href="{% static 'djangoapp/img/favicon.png' %}" />
<link rel="apple-touch-icon" href="{% static 'djangoapp/img/favicon.png' %}" />
</head>
<body>
<header></header>
<main>
{% block content %} {% endblock %}
</main>
<footer></footer>
<script type="text/javascript" src="{% static 'djangoapp/js/script.js' %}"></script>
</body>
</html>
色々と省略していますが、ポイントとなるのはblockテンプレートタグです。「{% block 名前 %} {% endblock %}」を記述すると、その部分に子のテンプレートから埋め込むことができます。
このように、Djangoでは様々なテンプレートタグを使って動的にHTMLを生成できます。
以下は子テンプレートのサンプルです。
{% extends 'djangoapp/base.html' %}
{% block content %}
<div>indexページです</div>
{% endblock %}
親テンプレートを継承するには、上記のようにextendsテンプレートタグを使います。そして、親と同じ名前のblockテンプレートタグで囲った部分が、親テンプレートに埋め込まれます。
このblockテンプレートタグは、名前の部分を変えれば自由に増やせます。例えば、ページごとにタイトル(titleタグ)を変更したい場合は「block title」といったテンプレートタグを親テンプレートに用意します。
静的ファイルの利用
ここでは、CSSやJavaScript、画像などの静的ファイルの利用方法を紹介します。
静的ファイルの置き場所は、プロジェクトとアプリケーションの2か所があります。複数のアプリケーションから共通で使用する静的ファイルはプロジェクトフォルダに置き、アプリケーションごとに必要な静的ファイルはアプリケーションフォルダに配置します。
今回の例では、プロジェクト単位の静的ファイルは「djangotest/static」というフォルダに格納して、アプリケーション単位の静的ファイルは「djangoapp/static/djangoapp」というフォルダに格納します。
- djangotest/static/css/base.css
- djangotest/djangoapp/static/djangoapp/css/style.min.css
- djangotest/djangoapp/static/djangoapp/js/script.js
- djangotest/djangoapp/static/djangoapp/img/favicon.png
テンプレートと同様で、複数のアプリケーションに同名の静的ファイルがあっても識別できるように、staticの後にアプリケーション名のフォルダを名前空間として用意しています。
DjangoはデフォルトではINSTALLED_APPSの「static」ディレクトリから静的ファイルを探します。それ以外の場所にも静的ファイルを用意する場合は、settings.pyを修正する必要があります。
例えば、プロジェクト単位の静的ファイルを利用する場合、STATIC_URLの下あたりにSTATICFILES_DIRSを追記して、静的ファイルの場所を伝えます。
STATICFILES_DIRS = (str(BASE_DIR / 'static'),)
テンプレートから静的ファイルを読み込むには、先ほどのテンプレートのサンプルのように、load staticテンプレートタグを最初に記述します。
{% load static %}
そして、以下のようにstaticテンプレートタグを使うと、静的ファイルのURLを生成します。
<link rel="stylesheet" type="text/css" href="{% static 'css/base.css' %}" />
<link rel="stylesheet" type="text/css" href="{% static 'djangoapp/css/style.css' %}" />
staticfilesアプリケーションがINSTALLED_APPSに登録されており、DEBUG = Trueの場合、開発サーバーを立ち上げるとSTATIC_URLから静的ファイルの配信が自動で行われます。
一方で本番環境では、静的ファイルの配信を手動で設定する必要があります。
本番環境で静的ファイルの配信を行う場合、アプリケーションごとに静的ファイルが分散していると不便です。その場合は、collectstaticというコマンドを使うと、静的ファイルを一か所に集めることができます。
python manage.py collectstatic
静的ファイルを集める場所は、settings.pyのSTATIC_ROOTに定義します。collectstaticを実行する際はSTATIC_ROOTがないとエラーになります。
ビューの作成
ここではviews.pyの作成を行います。
ビューは画面で入力された情報を取得したり、データベースにアクセスしたりと、様々なプログラムを記述します。
以下は非常に簡易的なビューのサンプルです。
def index(request):
return render(request, "djangoapp/index.html", {"message": "Test"})
renderメソッドには、第一引数にリクエストオブジェクト、第二引数に呼び出すテンプレート、第三引数にテンプレートに渡す情報を辞書の形式で指定します。
ビューから渡された情報をテンプレートに埋め込むには、以下のように記述します。
<div>{{ message }}</div>
上記の例では「Test」という文字が画面に表示されるだけですが、実際にはデータベースから取得した情報などを渡すケースが多いと思います。
例えば、以下のサンプルではユーザー情報の一覧をデータベースから取得してテンプレートに渡しています。(別途モデルを作成する必要があります)
def index(request):
users = User.objects.all()
return render(request, "djangoapp/index.html", {"users": users})
ビューの中身は自由に記述できますが、処理の最後にはHttpResponseオブジェクトを返すか、例外を投げる必要があります。上記のrenderメソッドを実行すると、テンプレートを基にしたHttpResponseオブジェクトが返ります。
例外を投げる場合には以下のように記述します。
raise Http404()
アプリケーションのURLディスパッチャを作成
アプリケーションフォルダにurls.pyというファイルを作成して、アプリケーションごとのルーティングを定義します。
以下はurls.pyのサンプルです。
from django.urls import path
from . import views
app_name = "djangoapp"
urlpatterns = [
path('', views.index, name='index'),
path('detail/<int:user_id>/', views.user_detail, name='detail'),
]
app_nameにはアプリケーション名を設定します。これが名前空間となり、アプリケーションごとに同名のルーティングがあっても識別できます。
pathの第一引数にはURLを指定します。第二引数にはそのURLと一致したリクエストの場合に呼び出されるビューの関数を指定します。
上記の例では、システムのトップページはindexというビューが呼ばれ、「/ドメイン名/detail/8」といったURLの場合はuser_detailというビューが呼ばれます。
ここまでの手順でトップページが表示できるはずです。仮想環境に入って開発サーバーを立ち上げた状態で「http://127.0.0.1:8000/djangoapp/」にアクセスします。「indexページです」と表示されれば成功です。
モデルの作成
データベースへのアクセスが必要な場合、models.pyの中にモデルを作成します。
一つのモデルは一つのテーブルと対応しており、以下のようにデータベースの定義とメタ情報で構成されています。
from django.db import models
class User(models.Model):
id = models.AutoField(primary_key=True)
email = models.EmailField(max_length=40)
name = models.CharField(max_length=30)
description = models.TextField(max_length=200)
register_date = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = "user"
EmailFieldはメールアドレス、TextFieldは文章といったように、専用のクラスを使ってデータ型を定義します。引数にはprimary_key(主キー)やmax_length(最大文字数)などを指定できて、それらはデータベースにも反映されます。
メタ情報ではdb_tableにテーブル名を設定しています。この他にもメタ情報には色々と設定できるので、調べてみてください。
マイグレーション
モデルを作成したらマイグレーションを行います。
マイグレーションは、モデルの内容およびsettings.pyのデータベース設定に基づき、データベースの作成や更新などを行います。
まず最初に、以下のコマンドでmigrationsフォルダの中にマイグレーションファイルを作成します。
python manage.py makemigrations
マイグレーションファイルには、前回からの差分の変更が含まれています。例えば、マイグレーションを実行した後にモデルのメールアドレスの最大文字列を40文字から50文字に変更した場合、マイグレーションファイルにはその変更内容が記述されます。
マイグレーションファイルを使ってデータベースを更新します。以下のコマンドでマイグレーションが実行されます。
python manage.py migrate
このように、モデルを修正した場合は、データベースを更新するためにmakemigrationsとmigrateコマンドを毎回実行する必要があります。
Django adminの設定
Djangoには管理者用のツールが用意されており、ブラウザからデータベースのテーブルにレコードを追加・編集・削除できます。
先ほどマイグレーションを行ってuserテーブルを作成しましたが、それだけではDjango adminから操作できません。事前にモデルをadmin.pyに登録する必要があります。
from django.contrib import admin
from .models import User
admin.site.register(User)
次に、以下のコマンドでDjango adminにアクセスできるユーザーを作成します。
python manage.py createsuperuser
ユーザー名、パスワード、メールアドレスを登録しますが、メールアドレスが不要な場合はエンターを押すとスキップできます。
ユーザーを作成したら、開発サーバーを立ち上げて「http://127.0.0.1:8000/admin」のURLにログインします。 マイグレーションによって作成したテーブルが表示されるので、データの登録を試してみてください。
更新処理と入力検証
ここでは、画面から入力された情報を入力検証して、問題がなければデータベースに保存する方法を紹介します。これまでより少し難易度が上がります。
モデルフォームの作成
登録・編集ページを作成する場合、モデルフォームが便利です。
通常は、名前やメールアドレスなどの項目ごとにinputタグを書いて登録・編集ページを作ります。一方でモデルフォームを使うと、事前に定義した内容に基づいて入力フィールドを自動表示したり、入力検証をすることができます。
以下はモデルフォームのサンプルです。
from django.forms import ModelForm
from .models import User
class UserForm(ModelForm):
def __init__(self, *args, **kwargs):
super(UserForm, self).__init__(*args, **kwargs)
self.fields["email"].required = True
self.fields["name"].required = True
class Meta:
model = User
fields = '__all__'
labels = {
'id': 'ユーザーID',
'email': 'メールアドレス',
'name': '氏名',
'description': 'プロフィール'
}
error_messages = {
'email': {
'required': 'メールアドレスの入力は必須です。',
'max_length': 'メールアドレスは40文字以内で入力してください。',
},
'name': {
'required': '氏名の入力は必須です。',
'max_length': '氏名は30文字以内で入力してください。'
}
}
ポイントとしては、まず最初にModelFormクラスを継承します。そして、コンストラクタの中で必須入力の設定を行います。
また、Metaクラスを定義すると、画面に表示する項目やラベルを制御したり、入力検証のエラーメッセージを上書きできます。
テンプレートの作成
以下はユーザー情報を編集する画面のサンプルです。
{% extends 'djangoapp/base.html' %} {% block title %} ユーザー編集画面 {% endblock %}
{% block content %}
<h2>ユーザー編集</h2>
{% if form.non_field_errors %}
<ul>
{% for non_field_error in form.non_field_errors %}
<li>{{ non_field_error }}</li>
{% endfor %}
</ul>
{% endif %}
<form
action="{% url 'djangoapp:edit' user_id %}"
method="post"
>
{% include "./parts/user_form.html" %}
<button type="submit">更新</button>
</form>
{% endblock %}
テンプレートに渡されたモデルフォームのオブジェクト(form)から、エラーメッセージを取得してfor文でループします。non_field_errorsには特定のフィールドに紐づかないエラーメッセージが含まれています。
formタグのactionにはURLをハードコーディングせず、urlテンプレートタグを使って保守性を高めます。
urlテンプレートタグには、urls.pyのpathに登録したnameを指定します。今回はurls.pyの中で「djangoapp」という名前空間をつけたので、「djangoapp:edit」というように名前空間も同時に指定します。
includeテンプレートタグを使ってフォームの中身は別ファイルに分けていますが、必ずしも分ける必要はありません。
{% csrf_token %} {% for field in form %}
<div>
<div>
{{ field.label_tag }} {% if field.help_text %}<span>{{ field.help_text }}</span>{% endif %}
</div>
<div>{{ field }}</div>
{% if field.errors %}
<ul>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endfor %}
モデルフォームのオブジェクトからフィールド情報を取得して、ラベル(field.label_tag)や入力フィールド(field)、エラーメッセージ(field.errors)などを表示しています。
csrf_tokenテンプレートタグは、クロスサイトリクエストフォージェリの対策です。formタグの中に必ず含めるようにしましょう。
ビューの作成
def user_edit(request, user_id):
user = get_object_or_404(User, pk=user_id)
if request.method == "GET":
form = UserForm(instance=user)
return render(request, 'djangoapp/edit.html', {'form': form, "user_id": user_id})
if request.method == "POST":
form = UserForm(request.POST, instance=user)
if form.is_valid():
form.save()
messages.success(request, 'ユーザー情報を更新しました。')
return HttpResponseRedirect('/djangoapp/result')
else:
return render(request, 'djangoapp/edit.html', {"form": form, "user_id": user_id})
# 以下は不正なリクエスト
return render(request, 'djangoapp/error.html')
get_object_or_404は、引数のユーザーIDをもとにユーザー情報を取得して、見つからない場合は404エラーを投げます。
リクエストがGETの場合には、取得したユーザー情報からモデルフォームのオブジェクトを生成して、テンプレートに渡します。
POSTの場合には、request.POSTというオブジェクトから送信された情報を取得して、is_validメソッドで入力検証を行います。問題がない場合はデータベースに保存して、処理結果ページにメッセージと共にリダイレクトします。
ルーティングの設定
「edit/ユーザーID」というURLにアクセスしたときに、ビューのuser_editにマッピングさせます。
urlpatterns = [
path('edit/<int:user_id>/', views.user_edit, name='edit'),
]
開発サーバーを立ち上げて「http://127.0.0.1:8000/djangoapp/edit/ユーザーID」のURLにアクセスします。 ユーザーIDの部分には、Django adminで登録したユーザーの情報を設定してください。ユーザーの編集画面が表示されて、無事に更新できれば成功です。なお、処理結果画面は省略しています。