Python
Django
GitHub
python3

はじめてのDjango Part2 [実践編] ToDoリスト作成

はじめに

はじめてのDjango Part1 [概要解説編]
の続きです。
Part2は実践編となります。
簡単なToDoリストのWebアプリを作成します(よくあるやつです)。

利用するもの

・GutHub
・Python3.6
・Django2.0

Python,Gitのインストールは済んでいる前提です。

GitHub/Git操作

GitHubでリポジトリ作成
リポジトリ名:todolist_lite
README作成チェックを入れて作成

リポジトリ作成したら、ローカルにクローン

git clone https://github.com/ユーザ名/todolist_lite

READMEを編集し、何でも良いので記載

django tutorial todo hoge ver1.0

ローカルのGitの設定してファーストコミット

git config --list
git config --global user.name 任意の名前
git config --global user.email 任意の名前メールアドレス
git add .
git status
git commit -m "first commit"
git push -u origin master

コマンド操作

Djangoのインストール

pip install --upgrade pip
pip install django==2.0

Djangoプロジェクトを作成

django-admin startproject mysite

プロジェクト配下に移動して、アプリ(機能)作成

cd mysite
python manage.py startapp todo

Linuxの場合は以下のおまじないがプロジェクトの設定ファイルに必要
Unix/Windowsでも書いておいて不自由はないのでとりあえず記述しておく

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

ついでに日本語化も同じ設定ファイルでやっておく

mysite/mysite/settings.py
LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'

単体用サーバ起動
ポート番号はrunserver 8181とかで任意のものに変えられる
開発環境ではこれで十分。ちゃんとサーバ用意するならGunicornがおすすめ

python manage.py runserver
http://localhost:8000

動作確認
Djangoのページが開かれていればOK

View設定。Hello World!する
とりあえず関数ベース。HTTPのレスポンスとして、固定文言を返すだけの疎通確認

mysite/todo/views.py
from django.http import HttpResponse

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

todoアプリ内にurls.pyを作成し、記述
Djangoではアプリ(機能)単位でURLを切ることができる。
Django 2.0以降ではpath指定での記述が推奨されるが、古いバージョンだとurlで記述する
pathの第一引数がURLパターン、第二がViewの関数名、第三がこのpathに付ける名前

mysite/todo/urls.py
from django.urls import path

from . import views

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

todoアプリのurls.pyをプロジェクト設定(mysite/mysite)に読み込むよう設定
includeを使うことで親子関係的に別のアプリをURLパターンに取り込める

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

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

プロジェクトの設定ファイルにtodoアプリの存在を記述

mysite/mysite/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'todo'
]

サーバ起動して、Hello Worldを確認

python manage.py runserver
http://localhost:8000

ここまではDjangoで最初にほぼ必ず行う設定

Django ToDoアプリ作成開始

モデル作成。いわゆるDB/ORM定義。DBと紐づけるだけでなく、
コマンド操作でこのModel定義に沿ってSQLを発行できる
作成するテーブル名はPost,カラムはmessage,created_date

mysite/todo/models.py
class Post(models.Model):
    message = models.CharField(
        max_length=100,
        verbose_name="タスク",
    )
    created_date = models.DateTimeField(
        auto_now_add=True,
        verbose_name="登録日時",
        )

モデルを元にSQLを作成・実行するコマンド
繋がってるDB(今回はSQLite3)に勝手に流してくれる。便利

python manage.py makemigrations todo
python manage.py migrate todo

管理機能を簡単に作ろう

python manage.py createsuperuser

Username: admin
Email address: hoge@huga.com
Password: aiueo1234
Password (again): aiueo1234

管理機能で作成したPostテーブルが確認・編集できるようにしよう

mysite/todo/admin.py
from django.contrib import admin
from .models import Post

#レコード表示機能
class PostAdmin(admin.ModelAdmin):
    list_display = ('message','created_date',)
    list_display_links = ('message','created_date',)

admin.site.register(Post,PostAdmin)

サーバ起動して、DBを確認。ブラウザ上からデータ登録もできる

python manage.py runserver
http://localhost:8000/admin/

URL設定。CRUDに合わせて以下で設計(Updateは無し)
Read:トップページ。indexメソッドを紐づけ
Create:トップ/add。addメソッドを紐づけ
Delete:トップ/delete/id(数字)。deleteメソッドを紐づけ

mysite/todo/urls.py
urlpatterns = [
    path('', views.index, name='index'),
    path('add', views.add, name='add'),
    path('delete/<int:id>', views.delete, name='delete'),
]

forms.py(フォーム)作成
Djangoで用意しているforms関数を使う
Djangoでフォームを用意する場合は、formsをよく使う

mysite/todo/forms.py
from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ('message',)

View作成
各機能ごとにメソッドを作り、models,formsと紐づける

mysite/todo/views.py
from django.shortcuts import render,get_object_or_404
from django.http import HttpResponse,HttpResponseRedirect
from django.urls import reverse
from .models import Post
from .forms import PostForm

def index(req):
    posts = Post.objects.all()
    form = PostForm()
    context = {'posts': posts, 'form': form, }
    return render(req, 'todo/index.html', context)

def add(req):
    form = PostForm(req.POST)
    form.save(commit=True)
    return HttpResponseRedirect(reverse('index'))

def delete(req, id=None):
    post = get_object_or_404(Post, pk=id)
    post.delete()
    return HttpResponseRedirect(reverse('index'))

HTMLファイル作成。
todoフォルダ配下にtemplatesフォルダを作成し、さらにその下にtodoフォルダを作成し、
index.htmlファイルを作成

mkdir -p mysite/todo/templates/todo
touch mysite/todo/templates/todo/index.html

HTMLのheadのリンクはCDNとstaticのCSSを読み込み
CDNでいい感じに見た目を整えてくれるので、それに沿ったHTMLを書く

{ }でDjangoのコードを埋め込む
この文法なんじゃ?と思うかもしれないが、
これはDjango特有のTemplatesへ埋め込むコードの書き方なのでPythonのことは忘れて覚えてほしい

mysite/todo/templates/todo/index.html
{% load static %}
<!DOCTYPE html>
<html>
    <head>
        <title>タスク管理アプリ</title>
            <meta charset="utf-8">
            <!-- CDN CSS(Karma CSS)
            https://karmacss.com/en/latest/installation.html -->
            <link rel="stylesheet" href="https://unpkg.com/karma-css@latest/dist/karma.min.css">

            <!-- static配下のCSSファイルを参照 -->
            <link rel="stylesheet"
            href="{% static 'css/todo.css' %}" >
    </head>
    <body>
        <!-- 固定枠 -->
        <div class="container">
            <h2>ToDoリスト</h2>
            <!-- ToDoアプリ Post処理 -->
            <form action="{% url 'add' %}" method="post">
                {% csrf_token %}
                <div class="text">
                        {{ form.message }}
                </div>

                <!-- ToDo追加ボタン -->
                <div class="form-group">
                    <button type="submit" class="button">
                        ToDo追加
                    </button>
                </div>
            </form>

            <!-- Todoリスト表示 -->
            <table class="todo list table">
                <tbody>
                    <!-- for文回してtodoを取得、表示 -->
                    {% for post in posts %}
                        <tr>
                            <td>
                                <div class="message">{{ post.message }}</div>
                                <div>{{ post.created_date }}</div>
                            </td>
                            <td>
                                <form action="{% url 'delete' post.id %}" method="post">
                                    {% csrf_token %}
                                    <button class="button del">削除</button>
                                </form>
                            </td>
                        </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </body>
</html>

todoフォルダ配下にstaticフォルダを作成し、さらにその下にcssフォルダを作成し、
todo.cssファイルを作成
自前でもちょっとだけCSS書く

mkdir -p mysite/todo/static/css
touch mysite/todo/static/css/todo.css
mysite/todo/static/css/todo.css
h2 {
    font-size: 50px;
    color: #000000;
    font-weight: 800;
    font-family: Impact;
}
body {
    background: #fffcf9;
}
td {
    border-top-width: 3px;
    border-top-color: #ffe0c1;
    background: #fdf5e6;
}
.message {
    font-size: 20px;
    font-weight: 600;
}
.button {
    color: #fa8072;
    background: #fffff0;
}
.del{
    color: #a52a2a;
    background: #ffc0cb;
}

プロジェクトの設定ファイルに静的コンテンツ(CSS,JavaScript)へのPathを設定
今回はJavaScriptは使わない

mysite/mysite/settings.py
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "static"),
)

ToDoリストアプリ確認

python manage.py runserver
http://localhost:8000

こんな感じ

todo_app.gif

Viewの関数をクラスベースにする

なるべく単純で分かり易いコードで動作させたかったので、とりあえず関数だけでViewをコーディングしたが、
オブジェクト指向の言語として、カスタマイズ性のためにもクラスでロジックを定義するのが一般的。
Djangoでは、便利に使えるクラスベース汎用Viewが色々と存在するので、
それを継承してクラスを書き、as_view()によってViewで扱えるように関数化する形となる。
実際には汎用Viewには便利な関数が用意されているが、今回はなるべくコードを変えないで動かす

mysite/todo/views.py
from django.shortcuts import render,get_object_or_404
from django.http import HttpResponse,HttpResponseRedirect
from django.urls import reverse
from .models import Post
from .forms import PostForm
from django.views import View
from django.views.generic.edit import CreateView,DeleteView

#トップ画面表示
class Preview_Todo(View):
    def get(self, req, *args, **kwargs):
        posts = Post.objects.all()
        form = PostForm()
        context = {'posts': posts, 'form':form, }
        return render(req, 'todo/index.html', context)
index = Preview_Todo.as_view()

#ToDo追加機能
class Create_Todo(CreateView):
    def post(self, req, *args, **kwargs):
        form = PostForm(req.POST)
        form.save(commit=True)
        return HttpResponseRedirect(reverse('index'))
add = Create_Todo.as_view()

#ToDo削除機能
class Delete_Todo(DeleteView):
    def delete(self, req, id=None):
        post = get_object_or_404(Post, pk=id)
        post.delete()
        return HttpResponseRedirect(reverse('index'))
delete = Delete_Todo.as_view()
python manage.py runserver
http://localhost:8000

動作確認して問題なければ、GitHubへPushして終了

git add .
git status
git commit -m "create todolist app ver 1.0"
git push origin master

おわり

ソースコード
https://github.com/Umetea18/todolist_lite

お疲れ様でした