Python + Django で作る簡易To-doリスト

#Djangoでアプリケーションを作るにあたって

前回Djangoで作る自分だけのTwitterクライアントhttp://qiita.com/Gen6/items/11fa5265053da95fcf0b、

をやりましたので今回はSQLiteを使った簡易的なTo-doリストアプリケーションを作成していきたいと思います。色々カスタマイズ余地を残すべく出来る限りシンプルな構成としてみました。To-doリストから自分へTwitterでリプライを送るなどの改造余地もあるかと。

今回はDjangoのFormを活用して作っていきます。
これが理解できるようになると簡易的な顧客管理システムや、掲示板、SNSっぽいアプリケーションなどできる幅がぐっと広がるような気はします。

Djangoってそもそもどうやってはじめるのかは以下の記事を御覧ください。
http://qiita.com/Gen6/items/1848f8b4d938807d082e

#前準備

すでにPython3とDjangoが導入されており、virtualenvもインストールされている前提で進めていきます。ここまでの前準備ができていない方は、過去記事を参考に前準備をしてください。

#プロジェクトの作成

Djangoプロジェクトをorders とし、配下にmyapp を作成します。

```
virtualenv) $ django-admin startproject orders
virtualenv) $ cd orders
virtualenv) $ python manage.py startapp myapp
```

orders ディレクトリに　myapp と orders が作成されていることを確認してください。

##初期設定をする

###ordersディレクトリの作業

settings.pyの内容を以下を参考に記述してください。
データベースにはSQLite3をそのまま使います。

```orders/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp',
]

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates'),],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

STATIC_URL = '/static/'

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "static"),
)
```

###url.py

```orders/urls.py
from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^myapp/', include('myapp.urls')),
    url(r'^', include('myapp.urls',namespace='myapp')),
    url(r'^admin/', admin.site.urls),
]
```

namespaceを設定しておくことで後々便利になります。
namespaceをつける習慣をつけておくといいかと。

###myapp ディレクトリの作業

```myapp/ursl.py
from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.index, name='index'),
]
```

#アプリケーションを作成していく

##modelの定義

今回は簡単なTo-doリストなのですが、Djangoに組み込まれているフォームを活用していきますのでざっくりとモデルを定義します。models.DateTimeField()は便利です。

```myapp/models.py
from django.db import models

class Posting(models.Model):
    message = models.CharField(
        max_length = 140,
        verbose_name = 'やること',
    )

    created_at = models.DateTimeField(
        auto_now_add = True,
        verbose_name= '日時',
    )
```

やること、をテキストで受取し日時は自動で書き込まれるようにします。受け取る文字数は140文字まで、としてみました。

##formの準備をする

ディレクトリ内に新規で作成します。

```myapp/forms.py
from django import forms
from myapp.models import Posting


class PostingForm(forms.ModelForm):

    class Meta:
        model = Posting
        fields = ('message',)
        widgets = {
            'message': forms.Textarea(attrs={'cols': 40, 'rows': 4})
        }
```

フォームのフィールドには他にも利用できるフィールドがあるのですが、今回はTo-doだけなのでこのような記述となっています。widgetsでHTMLに書き出された場合のテキストエリアの大きさなどを設定可能です。

##viewを記述していく

importでモデルクラスをインポートし、Django PostingFormをインポート。
今回はページ遷移なしなので、redirect()を活用します。ここでnamespaceを設定した理由がわかるかと思います。

```myapp/views.py
import sys, codecs
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)

from django.http.response import HttpResponse
from django.shortcuts import (render, redirect,)
from django import forms
from myapp.models import Posting
from .forms import PostingForm

def index(request):
    form = PostingForm(request.POST or None)
    if request.method == 'POST':
        if form.is_valid():
            form.save()
            return redirect('myapp:index')
    new_text = Posting.objects.all().order_by('-id')
    contexts = {
        'form':form,
        'new_text':new_text,
    }
    return render(request, 'index.html', contexts)
```

ここでは

```
new_text = Posting.objects.all().order_by('-id')
```

によってデータベースの内容をID逆順に表示させる指示を書いています。
.order_by() は色々やり方があるので、アプリケーションによって変えてみるといいかと。

```
contexts = {
        'form':form,
        'new_text':new_text,
    }
```

ここでDjangoのフォームを渡します。同時にデータベースの内容を表示したいので、辞書この2つをrender関数の第3引数にします。テンプレートで使いたいものはどんどん辞書で渡してやるといいと思います。

##admin.pyを設定しておく

```myapp/admin.py
from django.contrib import admin
from myapp.models import Posting


class PostingAdmin(admin.ModelAdmin):
    list_display = ('id','message','created_at')

admin.site.register(Posting,PostingAdmin)
```

Django管理サイトから確認するために、表示項目を設定しておきましょう。
```
list_display = ('id','message','created_at')
```
このように書くことでidとメッセージ、投稿日時を表示できるようになります。

##HTMLテンプレートを準備する

```templates/base.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="../static/css/bootstrap.min.css" rel="stylesheet">
    <link href="../static/css/custom.css" rel="stylesheet">
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <script src="../static/js/bootstrap.min.js"></script>
    <title></title>
  </head>
  <body>
    {% block body %}
    {% endblock %}
  </body>
</html>
```

ベースとなる共通パーツです。
staticディレクトリにBootstrapを入れています。リンク構造はテンプレート用に書いてもいいのですが、参考として従来通りの書き方としてみました。

```
{% static 'css/custom.css' %}
```

というような記述が可能です。

```tenplates/index.html
{% extends "base.html" %}
{% block body %}
  <div class="container">
    <div class="row">
      <div class="col-md-12">
        <form action="{% url 'myapp:index' %}" method="post">
          <div class="row">
            {% for field in form %}
            <label class="col-sm-3">{{ field.label_tag }}</label>
            <label class="col-sm-7">{{ field }}</label>
            {% endfor %}
            <input type="submit" class="btn btn-primary" value="登録">
            {% csrf_token %}
            </div>
          </div>
        </form>
        {% include "to_do.html" %}
      </div>
    </div>
  </div>
{% endblock %}
```

フォームを表示するメインのHTMLファイルとなります。
入力したTo-doリストを表示するために別のHTMLファイルをインクルードします。

urls.pyでnamespaceを設定しているので、

```
<form action="{% url 'myapp:index' %}" method="post">
```

このような書き方ができるようになります。便利ですね！

```
{% csrf_token %} 
```

これはクロスサイトリクエスト・フォージェリ対策になってます。HIDDENに暗号論的擬似乱数値を埋め込むので、これを書き忘れるとPOSTできません。必ずフォームを閉じる前に設置します。<form> のすぐ後ろに配置したりすると忘れにくいかと。

```templates/to-do.html
<div class="row">
  {% for i in new_text %}
  <div class="col-md-4">
  <p>{{ i.message }} - {{ i.created_at }}</p>
  </div>
  {% endfor %}
</div>
```

index.htmlにインクルードした中身となります。
単純にデータベースから取得した内容をforで回しているだけですね。


#migrateする

```
virtualenv) $ python manage.py makemigrations myapp
virtualenv) $ python manage.py migrate
```

#アプリケーション画面

<img width="684" alt="スクリーンショット 2016-11-14 13.11.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/71585/7c798936-9139-c07a-2fe1-e35f2ede0e18.png">

完成です。