Django入門 (チュートリアル)

トップ > Django入門 > チュートリアル

Django とは

インストール

CentOS 7 に Django 2.2 をインストールします。

Shell
# yum -y install https://centos7.iuscommunity.org/ius-release.rpm
# yum -y install python36u python36u-pip
# pip3.6 install --upgrade pip
# pip3.6 install Django==2.2
# django-admin --version

SQLite3をバージョンアップする

データベースに SQLite を使用する場合、Django 2.2 は SQLite のバージョンが古いと、「django.core.exceptions.ImproperlyConfigured: SQLite 3.8.3 or later is required (found 3.7.17).」の様なエラーとなるので、SQLite を最新版にバージョンアップします。

Shell
# yum install -y wget gcc make
# wget https://www.sqlite.org/2019/sqlite-autoconf-3290000.tar.gz
# tar zxvf ./sqlite-autoconf-3290000.tar.gz
# cd ./sqlite-autoconf-3290000
# ./configure --prefix=/usr/local
# make
# make install
# cd ..
# rm -rf ./sqlite-autoconf-3290000 ./sqlite-autoconf-3290000.tar.gz
# mv /usr/bin/sqlite3 /usr/bin/sqlite3_old
# ln -s /usr/local/bin/sqlite3 /usr/bin/sqlite3
# echo 'export LD_LIBRARY_PATH="/usr/local/lib"' >> ~/.bashrc
# source ~/.bashrc

プロジェクトを作成する

まず、Django プロジェクトを作成します。Django では、コンフィグディレクトリの名前もプロジェクト名と同じになってしまうため、一度、config という名前でプロジェクトを作成し、その後、ディレクトリ名を変更するのがおすすめです。

Shell
$ django-admin startproject config
$ mv ./config ./myproj
$ cd ./myproj

下記のファイルが作成されます。

Files
./myproj
./myproj/manage.py
./myproj/config
./myproj/config/__init__.py
./myproj/config/settings.py
./myproj/config/urls.py
./myproj/config/wsgi.py

簡易サーバを起動する

外部のクライアントから接続すると 「Invalid HTTP_HOST header: '192.168.xx.xx'. You may need to add '192.168.56.102' to ALLOWED_HOSTS.」 といったエラーとなることがあります。./config/settings.py に接続を許可するホストの情報を設定する必要があります。開発時はとりあえず、すべて('*')を指定していてもよいですが、本番移行の際にはセキュリティ確保のため、HTTP の Host ヘッダで送信されてくる IPアドレスや URL を指定してください。

./config/settings.py
ALLOWED_HOSTS = ['*']

開発用の簡易サーバを起動します。LISTEN する IPアドレスとポート番号を指定することができます。

Shell
$ python3.6 manage.py runserver 0.0.0.0:80

80番ポート番号が解放されていない場合は、例えば下記の様にして開放する必要があります。

Shell
# yum -y install firewalld
# systemctl enable firewalld
# systemctl start firewalld
# firewall-cmd --add-port 80/tcp --permanent
# firewall-cmd --reload

ブラウザから http://サーバアドレス/ にアクセスすることで、Django のサンプルページが表示されれば成功です。

アプリケーションを作成する

books アプリケーションを作成します。

Shell
$ python3.6 manage.py startapp books

下記のファイルが作成されます。

Files
./books/__init__.py
./books/admin.py
./books/apps.py
./books/models.py
./books/tests.py
./books/views.py
./books/migrations/__init__.py

./books/apps.py に定義されたクラス名をアプリケーションとして ./config/settings.py の INSTALLED_APPS に登録します。

./config/settings.py
INSTALLED_APPS = [
    'books.apps.BooksConfig',
    'django.contrib.admin',
    'django.contrib.auth',

./config/urls.py に、http://サーバアドレス/books/ が要求されたら、./books/urls.py を参照するように指定します。

./config/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', include('books.urls')),
]

./books/urls.py ファイルを新規に作成し、http://サーバアドレス/books/ の次に何もなければ、view.py の list_books 関数を呼び出すように指定します。

./books/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.list_books, name='list_books'),
]

./books/views.py ビューを下記の様に修正します。

./books/views.py
from django.http import HttpResponse

def list_books(request):
    return HttpResponse("Hello world!")

ブラウザで http://サーバアドレス/books/ にアクセスして Hello world! が表示されれば成功です。

アプリケーションディレクトリを集約する

Django の標準では BASE_DIR(./) 直下にアプリケーションディレクトリが乱雑に並んでしまうため、アプリケーションを集約して格納するための ./apps ディレクトリを用意します。

Shell
$ mkdir ./apps

./apps ディレクトリを ./config/settings.py に登録します。

./config/settings.py
import os
import sys

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))

アプリケーションディレクトリを ./apps 配下に移動します。

Shell
$ mv ./books ./apps

ブラウザで http://サーバアドレス/books/ にアクセスして Hello world! が表示されれば成功です。

モデルを作成する

本(Book)を管理するモデルを作成します。本(Book)は、管理番号(book_id)、タイトル(title)、著者(author)の属性を持つものとします。

./apps/books/models.py
from django.db import models

class Book(models.Model):
    book_id = models.CharField(max_length=32)
    title = models.CharField(max_length=256)
    author = models.CharField(max_length=256)

    def __str__(self):
        return self.title

DB に対するマイグレーションファイルを作成し、マイグレーションを実行します。これにより、モデルで定義したテーブルやカラムが自動的に作成されます。テーブルやカラムを変更して再度マイグレーションを行うことで、テーブル追加やカラム追加がマイグレーションされます。

Shell
$ python3.6 manage.py makemigrations
$ python3.6 manage.py migrate

作成されたテーブルは次のようにして確認することができます。

Shell
$ python3.6 manage.py dbshell
sqlite> .tables
auth_group                  books_book
    :                            :
sqlite> .schema books_book
CREATE TABLE IF NOT EXISTS "books_book" (...);
sqlite> select * from books_book;    // まだ何も表示されない
sqlite> (Ctrl-D)

管理者サイトを使用する

Django は簡易的な管理サイト機能を標準で装備しています。管理者サイトからモデルで定義した DB に対して簡単な追加・一覧・更新・削除を行うことができます。まず、管理者ユーザを作成します。

Shell
$ python3.6 manage.py createsuperuser
Username (leave blank to use 'root'): admin
Email address: admin@example.com
Password: ***password***
Password (again): ***password***
Superuser created successfully.

Books アプリケーションの Book モデルを管理者サイトで管理できるようにします。

./apps/books/admin.py
from django.contrib import admin
from .models import Book

admin.site.register(Book)

ブラウザから http://サーバアドレス/admin/ にアクセスすることで、上記で作成した管理者ユーザで管理者サイトにログインすることができます。Groups と Users のみ表示され、Books が表示されない場合は、「manage.py runserver」を再起動してみてください。管理者サイトから、Book の情報を何冊が登録してみましょう。

テンプレートを使用する

HTMLテンプレートを使用して Book の一覧をテーブル表示してみます。まず、テンプレートディレクトリを作成します。

Shell
$ mkdir ./templates

./templates を ./config/settings.py に登録します。

./config/settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
           :
    },
]

Books のためのテンプレートディレクトリを作成します。

Shell
$ mkdir ./templates/books

./templates/books の下に list_books.html テンプレートファイルを作成します。

./templates/books/list_books.html
<table>
  <thead>
    <tr>
      <th>Book ID</th>
      <th>Title</th>
      <th>Author</th>
      <th>Action</th>
    </tr>
  </thead>
  <tbody>
    {% if books %}
      {% for book in books %}
        <tr>
          <td><a href="/books/{{ book.book_id }}">{{ book.book_id }}</a></td>
          <td>{{ book.title }}</td>
          <td>{{ book.author }}</td>
          <td><a href="/books/{{ book.book_id }}/edit">[Edit]</a></td>
        </tr>
      {% endfor %}
    {% else %}
      <tr>
        <td colspan=4>No books.</td>
      </tr>
    {% endif %}
  </tbody>
</table>

./apps/books/views.py ファイルを下記の様に修正します。

./apps/books/views.py
from django.http import HttpResponse
from django.template import loader
from .models import Book

def list_books(request):
    books = Book.objects.all()
    context = {
        'title': 'List Books',
        'books': books,
    }
    template = loader.get_template('books/list_books.html')
    return HttpResponse(template.render(context, request))

http://サーバアドレス/books/ にアクセスして、管理者サイトで登録した本の一覧が表示されれば成功です。

テンプレートで共通なレイアウトページを参照する

{% block %} や {% extends %} を用いて、複数のテンプレートで共有するレイアウトを作成することができます。子テンプレート(list_books.html)で定義した content という名前のブロックを、親テンプレート(layout.html)が参照して表示します。まず、./templates/layout.html を新規作成します。

./templates/layout.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{ title }}</title>
</head>
<body>
  <h1>{{ title }}</h1>
  {% block content %}{% endblock %}
</body>
</html>

テンプレートファイルに下記を追記します。

./templates/books/list_books.html
{% extends 'layout.html' %}
{% block content %}
<table>
  :
</table>
{% endblock %}

http://サーバアドレス/books/ にアクセスして、「List Books」のタイトルが表示されれば成功です。

スタティックファイルを読み込む

CSS や JavaScript などのスタティックファイルを格納するスタティックディレクトリ(./static)を作成します。

Shell
$ mkdir ./static
$ mkdir ./static/css ./static/js ./static/img

./static を ./config/settings.py の末尾に登録します。

./config/settings.py
    :
STATIC_URL = '/static/'

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

./static/css/style.css ファイルを作成します。

./static/css/style.css
table { border-collapse: collapse; margin-bottom: .5rem; }
table th, table td { border: 1px solid #999; padding: .1rem .3rem; }
table th { background-color: #ddd; }
button { line-height: 1.2rem; min-width: 6rem; }

レイアウトファイルから style.css を読み込ませます。

./templates/layout.html
<head>
<meta charset="utf-8">
<title>{{ title }}</title>
<link rel="stylesheet" href="/static/css/style.css">
</head>

http://サーバアドレス/books/ にアクセスして、表の枠線が表示されたら成功です。うまく表示されない場合は、「manage.py runserver」を再起動したり、ブラウザのキャッシュをクリアしてみましょう。

ヘッダやメニューバーを表示する

レイアウトファイルにヘッダやメニューバーを追加します。

./templates/layout.html
<body>
  <div class="header-block">
    @ Django Sample
  </div>
  <div class="menu-block">
    <a href="/books/">Book</a>
    <a href="/settings/">Settings</a>
  </div>
  <div class="content-block">
    <h1>{{ title }}</h1>
    {% block content %}{% endblock %}
  </div>
</body>

ヘッダやメニューのためのスタイルを追記します。

./static/css/style.css
* { margin: 0; padding: 0; }
.header-block { background-color: #000; color: #fff; line-height: 2rem;
    font-weight: bold; padding: 0 .5rem; }
.menu-block { background-color: #ddd; line-height: 2rem; padding: 0 .5rem; }
.content-block { padding: 0 .5rem; }
table { border-collapse: collapse; margin-bottom: .5rem; }

http://サーバアドレス/books/ にアクセスして、ヘッダやメニューが表示されれば成功です。

詳細画面

一覧画面と同様に詳細画面を作成します。まず、テンプレートを作成します。

./templates/books/detail_book.html
{% extends 'layout.html' %}
{% block content %}
<table>
  <tr><th>Book ID</th><td>{{ book.book_id }}</td></tr>
  <tr><th>Title</th><td>{{ book.title }}</td></tr>
  <tr><th>Author</th><td>{{ book.author }}</td></tr>
</table>
<div class="basic-block">
  <button onclick="location.href='/books/'">Return</button>
</div>
{% endblock %}

./apps/books/views.py に詳細画面用のビュー関数を追加します。

./apps/books/views.py
    :
def detail_book(request, book_id):
    try:
        book = Book.objects.get(book_id=book_id)
    except Book.DoesNotExist:
        book = None

    context = {
        'title': 'Detail Book',
        'book': book,
    }
    template = loader.get_template('books/detail_book.html')
    return HttpResponse(template.render(context, request))

ビューを urls.py に登録します。

./apps/books/urls.py
urlpatterns = [
    path('', views.list_books, name='list_books'),
    path('<str:book_id>', views.detail_book, name='detail_book'),
]

http://サーバアドレス/books/ から Book ID のリンクをクリックして、詳細画面が表示されれば成功です。

編集画面

詳細画面と同様に編集画面を作成します。まず、テンプレートを作成します。{% url 名前 引数 %} は、urls.py の name="..." で指定した名前に対応するパス名を取得します。{% if ... %} ... {% elif ... %} ... {% endif %} は条件式を記述します。

./templates/books/edit_book.html
{% extends 'layout.html' %}
{% block content %}
<form method="POST" action="{% url 'edit_book' book.book_id %}">
  {% csrf_token %}
  <input type="hidden" name="mode" value="{{ mode }}">
  <table>
    <tr>
      <th>Book ID</th>
      <td><input type="text" name="book_id" readonly value="{{ book.book_id }}"></td>
    </tr>
    <tr>
      <th>Title</th>
      <td><input type="text" name="title"
          {% if mode != 'input' %}readonly{% endif %} value="{{ book.title }}"></td>
    </tr>
    <tr>
      <th>Author</th>
      <td><input type="text" name="author"
          {% if mode != 'input' %}readonly{% endif %} value="{{ book.author }}"></td>
    </tr>
  </table>
  <div class="basic-block">
    {% if mode == 'input' %}
      <button type="button" onclick="location.href='{% url 'list_books' %}'">Return</button>
      <button type="submit">OK</button>
    {% elif mode == 'confirm' %}
      <button type="button" onclick="history.back()">Back</button>
      <button type="submit">OK</button>
    {% elif mode == 'result' %}
      <button type="button" onclick="location.href='{% url 'list_books' %}'">Return</button>
    {% endif %}
  </div>
</form>
{% endblock %}

ビューファイルに編集画面用のビューを追加します。

./apps/books/views.py
def edit_book_input(request, book_id):
    try:
        book = Book.objects.get(book_id=book_id)
    except Book.DoesNotExist:
        book = None

    context = {
        'title': 'Edit Book(input)',
        'mode': 'input',
        'book': book,
    }
    template = loader.get_template('books/edit_book.html')
    return HttpResponse(template.render(context, request))

def edit_book_confirm(request, book_id):
    book = Book()
    book.book_id = request.POST['book_id']
    book.title = request.POST['title']
    book.author = request.POST['author']

    context = {
        'title': 'Edit Book(confirm)',
        'mode': 'confirm',
        'warning_message': 'Are you sure you want to save?',
        'book': book,
    }
    template = loader.get_template('books/edit_book.html')
    return HttpResponse(template.render(context, request))

def edit_book_result(request, book_id):
    try:
        book = Book.objects.get(book_id=book_id)
        book.book_id = request.POST['book_id']
        book.title = request.POST['title']
        book.author = request.POST['author']
        book.save()
    except Book.DoesNotExist:
        book = None

    context = {
        'title': 'Edit Book(result)',
        'mode': 'result',
        'success_message': 'Success!',
        'book': book,
    }
    template = loader.get_template('books/edit_book.html')
    return HttpResponse(template.render(context, request))

def edit_book(request, book_id):
    if request.method == 'GET':
        return edit_book_input(request, book_id)
    elif request.method == 'POST':
        if request.POST['mode'] == 'input':
            return edit_book_confirm(request, book_id)
        if request.POST['mode'] == 'confirm':
            return edit_book_result(request, book_id)

ビューを urls.py に登録します。

./apps/books/urls.py
urlpatterns = [
    path('', views.list_books, name='list_books'),
    path('<str:book_id>', views.detail_book, name='detail_book'),
    path('<str:book_id>/edit', views.edit_book, name='edit_book'),
]

レイアウトにメッセージ表示欄を追加します。

./templates/layout.html
  <div class="content-block">
    <h1>{{ title }}</h1>
    {% if success_message %}
    <div class="msg-success">{{ success_message }}</div>
    {% endif %}
    {% if warning_message %}
    <div class="msg-warning">{{ warning_message }}</div>
    {% endif %}
    {% if error_message %}
    <div class="msg-error">{{ error_message }}</div>
    {% endif %}
    {% block content %}{% endblock %}
  </div>

style.css にスタイルを追加します。

./static/css/style.css
button { line-height: 1.2rem; min-width: 6rem; }
input[type="text"], select { border: 1px solid #ccc; height: 1.5rem;
    border-radius: .2rem; padding: 0 .3rem; width: 20rem; }
input[readonly] { border: 0; }
.msg-success { padding: .2rem; color: #080; border: 1px solid #9c9;
    background-color: #cfc; margin-bottom: .5rem; }
.msg-warning { padding: .2rem; color: #880; border: 1px solid #cc9;
    background-color: #ffc; margin-bottom: .5rem; }
.msg-error   { padding: .2rem; color: #800; border: 1px solid #c99;
    background-color: #fcc; margin-bottom: .5rem; }

http://サーバアドレス/books/ から [Edit] のリンクをクリックして、編集操作ができれば成功です。

多言語対応

Webページを、多言語に対応させます。まず、gettext をインストールします。

Shell
# yum install -y gettext

./config/settings.py に下記の設定を行います。LocaleMiddleware は必ず SessionMiddleware と CommonMiddleware の間に記述してください。

./config/settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'django.middleware.common.CommonMiddleware',
       :
]

LANGUAGE_CODE = 'ja'

LOCALE_PATHS = (
    os.path.join(BASE_DIR, 'locale'),
)

テンプレートの冒頭で i18n を読み込み、翻訳対象の文字列を {% trans ... %} で囲みます。

./templates/books/list_books.html
{% extends 'layout.html' %}
{% load i18n %}
{% block content %}
<table>
  <thead>
    <tr>
      <th>{% trans 'Book ID' %}</th>
      <th>{% trans 'Title' %}</th>
      <th>{% trans 'Author' %}</th>
      <th>{% trans 'Action' %}</th>
    </tr>
  </thead>

プログラムの中で翻訳対象の文字列を _(...) で囲みます。

./apps/books/views.py
from django.utils.translation import gettext_lazy as _

def list_books(request):
    books = Book.objects.all()
    context = {
        'title': _('List Books'),
        'books': books,
    }

./locale フォルダを作成し、翻訳対象の辞書ファイルを作成します。

Shell
$ python3.6 manage.py makemessages -l ja

./locale/ja/LC_MESSAGES/django.po ファイルが作成されるので、これに、翻訳対象文字列の翻訳を記入します。

./locale/ja/LC_MESSAGES/django.po
#: apps/books/views.py:9
msgid "List Books"
msgstr "ブックの一覧"

#: templates/books/list_books.html:7
msgid "Book ID"
msgstr "ブックID"

下記のコマンドを実行して、メッセージをコンパイルし、./locale/ja/LC_MESSAGES/django.mo ファイルを作成します。

Shell
$ python3.6 manage.py compilemessages

http://サーバアドレス/books/ にアクセスし、「List Books」の代わりに「ブックの一覧」と日本語が表示されれば成功です。うまく表示できない場合は、「manage.py runserver」を再起動したり、ブラウザキャッシュをクリアしてみましょう。

言語設定画面を用意する

言語設定画面を用意します。まずは、設定画面のためのアプリケーションとテンプレートディレクトリを作成します。

Shell
$ python3.6 manage.py startapp settings
$ mv ./settings ./apps
$ mkdir ./templates/settings

テンプレートファイルを作成します。

./templates/settings/settings.html
{% extends 'layout.html' %}
{% load i18n %}
{% block content %}
<form action="{% url 'set_language' %}" method="post">
  {% csrf_token %}
  <input name="next" type="hidden" value="{{ redirect_to }}">
  <select name="language">
    {% get_current_language as LANGUAGE_CODE %}
    {% get_available_languages as LANGUAGES %}
    {% get_language_info_list for LANGUAGES as languages %}
    {% for language in languages %}
      <option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected{% endif %}>
        {{ language.name_local }} ({{ language.code }})
      </option>
    {% endfor %}
  </select>
  <button>{% trans 'Set' %}</button>
</form>
{% endblock %}

views.py および urls.py を設定します。

./apps/settings/views.py
from django.http import HttpResponse
from django.template import loader
from django.utils.translation import gettext_lazy as _

def settings(request):
    context = {
        'title': _('Settings'),
    }
    template = loader.get_template('settings/settings.html')
    return HttpResponse(template.render(context, request))
./apps/settings/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.settings,  name='settings'),
]
./config/urls.py
urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', include('books.urls')),
    path('i18n/', include('django.conf.urls.i18n')),
    path('settings/', include('settings.urls')),
]

選択肢として表示する言語を絞るには、./config/settings.py で LANGUAGES を設定してください。

./config/settings.py
import os
import sys
from django.utils.translation import gettext_lazy as _
   :
   :

LANGUAGE_CODE = 'ja'

LANGUAGES = [
    ('ja', _('Japanese')),
    ('en', _('English')),
]

Settings メニューから http://サーバアドレス/settings/ にアクセスして、表示言語を切り替えることができれば成功です。


Copyright (C) 2018-2019 杜甫々
初版:2018年3月4日 最終更新:2019年7月15日
http://www.tohoho-web.com/django/tutorial.html