Skip to content
MinoruInachi edited this page Jul 8, 2018 · 8 revisions

Python札幌 Django 勉強会

OSC 2018 Hokkaido の Django セミナーでのデモを実装してみよう

資料: Djangoフレームワークの紹介

この資料の中にデモとして載っている ToDo アプリ(タスク管理、やることリスト)をハンズオン形式で実装します。

仮想環境

元のPython環境を汚さないため、今回は Python の仮装環境を使います。

Python本家の場合 (virtualenv)

Linux

$ python3 -m venv venv
$ . venv/bin/activate

Ubuntu の場合、以下を入れておかないとダメかもしれない。

$ apt-get install python3-venv

Windows:

> python -m venv venv
> venv/Scripts/activate

Anacondaの場合

Linux

$ conda create --name venv python
$ source activate venv

Windows

> activate venv

必須パッケージインストール

  • Django==2.0
  • docutils==0.14

Python本家

(venv) $ pip install django
(venv) $ pip install django-debug-toolbar
(venv) $ pip install docutils

Anaconda

(venv) $ conda install django
(venv) $ conda install -c conda-forge django-debug-toolbar
(venv) $ conda install docutils

プロジェクト作成

(venv) $ django-admin startproject myproject
myproject
 |-- manage.py           管理スクリプト
 |-- myproject
      |-- __init__.py    初期化処理スクリプト
      |-- settings.py    設定情報
      |-- urls.py        URL管理
      |-- wsgi.py        Webアプリのメイン

起動してみる

$ cd myproject
$ python manage.py runserver

Webプラウザで http://127.0.0.1:8000/ にアクセス

各種設定

myproject/settings.py をエディタでひらく。

データベース

デフォルトでは sqlite3 を使うように設定されている。このまま sqlite3 を使う。

# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

言語、タームゾーン

デフォルトでは、管理画面のUIやエラーメッセージ、日時の扱いが英語/標準時に設定されている。

# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

これを日本語/日本時間に設定するには以下のように設定を変更する。

# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

アプリケーション作成

todo という名前のアプリケーションを作成します。

(venv) $ python manage.py startapp todo

ディレクトリ (フォルダ)の構成は以下のようになります。

.
 |-- manage.py
 |-- myproject
 |    |-- __init__.py
 |    |-- settings.py
 |    |-- urls.py
 |    |-- mqq wsgi.py
 |-- todo
      |-- __init__.py
      |-- admin.py
      |-- apps.py
      |-- migrations
      |    |-- __init__.py
      |-- models.py
      |-- tests.py
      |-- views.py

todoの下に templatesディレクトリを作成しておきます。

      |-- templates/ 追加

Mac/Linux の場合

(venv) $ mkdir todo/templates

Windows の場合

(venv) > md todo\templates

アプリケーション名の登録

今回作成するアプリケーションの名前 todo を以下のように settings.py の INSTALLED_APPS リストに追加します。

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'todo',  # 追加
]

MTVアーキテクチャ

WebアプリではMVCアーキテクチャを採用しているものが多いです。

M モデル     データ構造
V ビュー      ページ
C コントロール モデルとビューをつなぐ制御

Django は MVC に似ていますが、MTV アーキテクチャというものを採用しています。 大部分は MVC アーキテクチャに似ていますが同じ「ビュー」という用語が別の意味で使われているので注意です。

M モデル     データ構造
T テンプレート ページのデザイン
V ビュー     ページの表示処理(その中でモデルとの入出力も扱う)

モデルの実装 (models.py)

TODOのデータを扱うモデルクラスを models.py にクラスとして定義します。 このクラスのインスタンス1つが、データベース上の1レコードに相当します。

データベースに登録する1レコードの内容は以下の3つの項目とします。

* 名称      タスク/やることを表す文字列 
* 完了      タスク/やることが完了したことを示すフラグ
* 作成日時   タスク/やることを作成した日時

これを Django のモデルとして表すと以下のようになります。

from django.db import models

# Create your models here.
class Todo(models.Model):
    name = models.CharField("名称", max_length=50)
    done = models.BooleanField("完了")
    created_at = models.DateTimeField("作成日時", auto_now_add=True)

    def __str__(self):
        return self.name

モデルのマイグレーション

モデルの定義内容をデータベースの管理情報として登録します。

(venv) $ python manage.py makemigrations todo
(venv) $ python manage.py migrate

モデルの名前の登録

todo/admin.py

Todoモデルを管理画面に登録

from django.contrib import admin
from .models import Todo

# Register your models here.
admin.site.register(Todo)

ビューの作成

フォームの作成

Todoモデルを編集するためのフォームクラスを作成します。 todo/forms.py を作成して、以下の内容を記述します。

from django import forms
from .models import Todo

class TodoForm(forms.ModelForm):
    class Meta:
        model = Todo
        fields = ['name', 'done']

ModelFormクラスを継承しているため、Todoモデルのフィールドから自動的にフォームフィールドが生成されます。

todo/views.py ファイルの編集

from django.shortcuts import render, redirect, get_object_or_404
from .models import Todo
from .forms import TodoForm

一覧表示

def index(request):
    # GETパラメータに ?all=1 と指定された場合は全件を表示
    if request.GET.get('all') == '1':
        queryset = Todo.objects.all()
    else:
        # 特に指定がない場合は未完了のレコードのみ
        queryset = Todo.objects.filter(done=False)
    # 作成日時で降順にソート
    todo_list = queryset.order_by('-created_at')
    return render(request, 'index.html', {'todo_list': todo_list})

追加

def add(request):
    # GETメソッドの場合は、データを渡していないフォーム
    # POSTメソッドの場合は、POSTされたデータを渡したフォームを生成
    form = TodoForm(request.POST or None)
    if form.is_valid():  # 入力内容の検証を実行(データが渡されていないフォームの場合は常にFalse)
        form.save()  # データベースに保存 (ModelForm.save()メソッド)
        return redirect('index')  # 一覧表示にリダイレクト
    return render(request, 'add.html', {'form': form})

編集

def edit(request, pk):
    # データベースから既存のレコードを取得
    # 指定されたpkと一致するレコードがない場合には404 NotFoundのレスポンスを返す

    todo = get_object_or_404(Todo, pk=pk)
    # 既存のデータ(Todoクラスのインスタンスを指定したフォーム)
    form = TodoForm(request.POST or None, instance=todo)
    if form.is_valid():
        form.save()  # データベースに保存
        return redirect('index')  # 一覧にリダイレクト
    return render(request, 'edit.html', {'form': form})

完了

def done(request, pk):
    # データベースから既存のレコードを取得
    todo = get_object_or_404(Todo, pk=pk)
    todo.done = True  # 完了扱いとする
    todo.save()  # データベースに保存
    return redirect('index')

myproject/urls.py の編集

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('todo.urls')),  # todoアプリケーションのurls.pyを含める
]

todo/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),  # 一覧表示
    path('add', views.add, name='add'),  # 追加
    path('<int:pk>/edit', views.edit, name='edit'),  # 編集
    path('<int:pk>/done', views.done, name='done'),  # 完了
]

テンプレート

一覧表示

todo/templates/index.html

<html><body>
<h1>TODO</h1>
<div>
<a href="{% url 'index' %}">未完了</a>
<a href="{% url 'index' %}?all=1">全部</a>
<a href="{% url 'add' %}">追加</a>
</div>

<ul>
{% for todo in todo_list %}
  <li>
    {% if todo.done %}
      <strike>{{ todo.name }}</strike>
    {% else %}
      {{ todo.name }}
    {% endif %}
    <a href="{% url 'edit' pk=todo.pk %}">編集</a>
    <a href="{% url 'done' pk=todo.pk %}">done</a>
  </li>
{% endfor %}
</ul>
</body></html>

追加

todo/templates/add.html

<html><body>
<h1>追加</h1>
<div>
<a href="{% url 'index' %}">未完了</a>
<a href="{% url 'index' %}?all=1">全部</a>
<a href="{% url 'add' %}">追加</a>
</div>

<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">保存する</button>
</form>

</body></html>

編集

todo/templates/edit.html

<html><body>
<h1>編集</h1>
<dev>
<a href="{% url 'index' %}">未完了</a>
<a href="{% url 'index' %}?all=1">全部</a>
<a href="{% url 'add' %}">追加</a>
</dev>

<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">保存する</button>
</form>

</body></html>

サーバ起動

(venv)$ pyhon manage.py runserver

仮想環境の無効化

virtualenv

(venv) $ deactivate

conda

Mac/Linux

(venv) $ source deactivate

Windows

(venv) > deactivate