1. models.pyにクラス定義
models.pyに、クラスを定義して例えば以下のように入力する。引数にはmodles.Modelを入れます。以下では、TodoModelというテーブルを作成し、itemという150字以内のフィールドと、doneというチェックを入れるためのブーリアンフィールドの二つを設定したことになります。ブーリアンフィールドのデフォルトをFalseにしているのはTodoなので作成した時点では未完了=falseだからです(True=完了)。他にどのようなフィールドがあるかはDjango公式サイトのmodels fieldで確認できます。
#★に関して:これを入れることで、後ほど出てくる管理画面にitemにおいて入力した内容がそのまま表示されるようにするためのものです。これをしないと”List object”という表示がされクリックしないと中身がわからなくなるためです。
from django.db import models class List(models.Model): item = models.CharField(max_length=150) done = models.BooleanField(default=False) def __str__(self): #★ return self.item
2. マイグレーション
作成したモデルをデータベースに反映させるための準備として、記述内容をデータベース言語にするためのマイグレーションをします。以下のようにターミナルでタイプします。
python manage.py makemigrations
マイグレーションが成功するとアプリ直下にmigrationsというフォルダが生成され、その中にマイグレーションされた(=データベースが読み取れる記述にアップデートされた)内容が記載されたファイルが生成されます。
3. マイグレート
続いて、実際にデータベースに入れるために、マイグレート、以下タイプします。
python manage.py migrate
4. admin.pyを修正(加筆)
Djangoの管理画面で操作できるようにadmin.pyを開き、以下のようにタイプします。
from django.contrib import admin from .models import List #★ admin.site.register(List)
この後に管理画面を見ると以下のように今回作成した”Lists”と表示があり、List モデルが表示されていることがわかります。※尚、管理画面のユーザー登録についてわからない方はこちらを確認ください。
上記のListsをクリックすると以下画面に遷移しますので”LISTを追加”をクリックします。
すると以下画面に遷移しますのでこれでTodoリストを管理画面から操作できるのがわかります。
5. フロントページに表示
結果を管理画面ではなく、フロントから確認できるように、表示させるためのページを作成しましょう。
1. views.pyを修正
まず、views.pyのインポート文を追加します。作成したListモデルが使用できるように#★の通り追記します。次に、任意の関数に#★★のようにタイプします。pythonはオブジェクト指向の言語なので、作成したListモデルに.objectsと繋げて更に.で繋げて用途に応じた既定の単語(メソッド)を入れるとその処理が反映されます。ここでは変数all_itemsにモデルListのすべての項目を入れるというコードです。#★★★は通常のテンプレートへの反映の型のコードですね。
from django.shortcuts import render from .models import List #★ def home(request): all_items = List.objects.all #★★ return render(request, 'todo/home.html', { 'all_items': all_items}) #★★★
2. htmlページの修正
続いてhtmlページに以下のようにテンプレートタグで先ほ定義したall_itemsを入れます。
{{ all_items }}
そうすると次のように反映されます。
さて、反映されましたがこれでは実用面から視認性が悪いのでBootstrap及びカスタムCSSで編集してみましょう。以下が例です。※ブーリアンフィールドやdeleteはまだ実装前であり、あくまでレイアウト・デザインのイメージ共有目的です。
{% if all_items %} <table class="table table-bordered"> <thead class="thead-dark"> {% for todos in all_items %} {% if todos.done %} <tr class="table-secondary"> <td class="text_bar">{{ todos.item }}</td> <td><center>{{ todos.done }}</center></td> <td>delete </td> </tr> {% else %} <tr> <td>{{ todos.item }}</td> <td><center>{{ todos.done }}</center></td> <td>delete </td> </tr> {% endif %} {% endfor %} </table> {% endif %}
ポイントは次の通りです。
・リストではなくテーブルを使用
・一行ずつ取り出すためにfor文を使用
・装飾のためにBootstrapのテーブルを使用
・ if文でTodo完了、未完了で処理を分けて装飾
= 完了したものはテーブル背景を変える
= タスク内容の文字を横線で消す
ここではカスタムCSSの方法を説明します。
3 カスタムCSSの設定方法
1. プロジェクト直下にstaticフォルダを作成、その中にCSSフォルダを作成し、その中に任意の名前のCSSファイルを作成
2. settings.pyを一番後ろに以下を追記
STATIC_URL = '/static/' #以下を追記 STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static') ]
3. 対象のテンプレートhtmlファイルの一番上に以下を追記。
{% load static %}
加えて、スタイルシートの箇所に以下を追記(cus_style.cssは任意のCSSファイル名)
<link rel="stylesheet" href="{% static 'css/cus_style.css' %}">
後は通常通り、適宜作成したCSSファイルに装飾内容を入れて、htmlでタグに入れれば完了です。
6. フロントから操作実装
さて、一通りモデルの作成から管理画面での確認、及びフロントページへの反映をみてきました。とはいえ、管理画面からではなくユーザーが直接フロントから入力できるようにしないと殆どのアプリは意味がありません。直接ユーザーがフロントから操作するための実装方法を説明します。
1. フォーム(forms.py)を作成
先ずはフォームの作成方法です。最初にアプリフォルダ直下にforms.pyファイルを作成します。ファイルには以下をタイプします。
#★ djangoからフォームを使えるようにするためにインポートしています。
#★★ フォーム内で作成したモデルを使えるようにインポートしています。
#★★★ classの名前の最初は大文字にしましょう。 引数はインポートしたformsからModelFormを入れます)
class MetaでこのListFormクラスが対象とするモデル及びフィールドを指定します。これらはmodels.pyで事前に設定した内容です。
from django import forms #★ from .models import List #★★ class ListForm(forms.ModelForm): #★★★ class Meta: model = List fields = ['item', 'done']
2. base.htmlを編集
フォームを入力するフロントページのhtmlにフォームが連動するようにコードを追記します。
#★ 「method=”POST”」を追記しています。POSTで飛ばすからねの意味です。
#★★ 「name=”item”」を追記しています。このinputフォームはmodels.pyで設定したitemだよの意味です。
<form class="form-inline my-2 my-lg-0" method="POST"> #★ {% csrf_token %} <input class="form-control mr-sm-2" type="search" placeholder="To-Do list" aria-label="Search" name="item"> #★★ <button class="btn btn-outline-secondary my-2 my-sm-0" type="submit">Add to list</button> </form>
3. views.pyを編集
作成したフォームが使用できるようにviews.pyにインポート文を追記します。
from .forms import ListForm
続いてviews.pyの対象の関数で以下のようにタイプします。
#★ POSTでリクエストが来た時に、ListFormにその内容を反映させ、変数formにいれてねの意味です
#★★ 入力内容にエラーがないかをis_validで確認しています。問題なければ、saveし、事前に作成したモデルのすべてのオブジェクトを変数all_itemsにいれて、home.htmlにレンダリングしてねの意味です。
#★★★else以降は、既存のデータを示してねの内容です。
if request.method == 'POST': #★ form = ListForm(request.POST or None) if form.is_valid():#★★ form.save() all_items = List.objects.all return render(request, 'todo/home.html', {'all_items': all_items}) else:#★★★ all_items = List.objects.all return render(request, 'todo/home.html', { 'all_items': all_items})
これで問題なく表示されます。
7. フォーム反映時のメッセージ文の表示
ユーザビリティの観点からフォームに入力した内容が反映される際に、メッセージを表示できるようにしましょう。
1. view.pyに追記
まずメッセージが利用できるようにdjangoのmessagesをインポートします。
from django.contrib import messages
次に、該当箇所に以下をタイプします。
messages.success(request, ('リストに追加されました'))
2. 表示させたいhtmlページに追記
該当箇所に以下をタイプします。もしメッセージがあれば、ある分だけfor文で出して表示してねの意味です。
{% if messages %} {% for msg in messages %} {{ msg }} {% endfor %} {% endif %}
そうすると以下のように表示されます。
3 bootstrapで見栄えをよくしましょう
先ほどのコード上で、次のようにdivで囲みます(bootstrap実装前提)#★
{% if messages %} {% for msg in messages %} <div class="alert alert-primary alert-dismissable" role="alert"> #★ <button class="close" data-dismiss="alert"> <small><sup>x</sup></small> </button> {{ msg }} </div> {% endfor %} {% endif %}
そうすると以下のようになります。ベターですね。
8. フロントページからモデルの削除の実装
1. urls.pyに追記
アプリのurls.pyのurlpatternsにpathを追記します。第一引数の<list_id>はモデルのプライマリーキーを当て込むためのものです。
path('delete/<list_id>', views.delete, name='delete')
2. views.pyに追記
delete関数を設定します。
#★ 関数の引数に先ほどurls.pyで設定したlist_idを加えます。
#★★ プライマリーキー(pk)がlist_idのListオブジェクトをitemに入れてねの意味です。
#★★★ 名前空間をurls.pyに入れているので引数はtodo:homeとなります。またインポート文にredirectも入れておきましょう。
from django.shortcuts import redirect def delete(request, list_id): #★ item = List.objects.get(pk=list_id) #★★ item.delete() messages.success(request, ('リストから削除されました')) return redirect('todo:home') #★★★
3. フロントページhtmlの編集
対象となる文字(例えば以下の場合ではDeleteの文字)を以下の通りタグで囲みます。todosはviews.pyにおいて、モデルオブジェクトをメソッドobjects.allですべて取り出して入れた変数名です。そこから.idを取り出しています。
<a href="{% url 'todo:delete' todos.id %}">delete</a>
これで完了です。
9. フロントページからクリックでモデルを操作
フロントページからモデルに関連する表記を操作してみたいと思います。ここではTodoリストのタスクの表記を線で消す、消さないを設定する想定です。※消す消さないのCSSコード等は出てきません。ポイントだけ掴んで頂ければ幸いです。
1. urls.pyに追記
アプリのurls.pyのurlpatternsにpathを追記します。これまで同様ですね。
path('delete/', views.delete, name='delete')
2. views.pyに追記
関数を設定します。ほぼ先ほどと同じです。
#★ タスクが完了している場合に線を引くという選択肢を与えるので、ここではクリック後にタスクが完了している状態としたいのでTrueにしてsaveします。
def cross_off(request, list_id): item = List.objects.get(pk=list_id) item.done= True #★ item.save() return redirect('todo:home')
3. フロントページhtmlの編集
これも先ほど同様です。対象となる文字(例えば以下の場合では完了の文字)を以下の通りタグで囲みます。
<a href="{% url 'todo:cross_off' todos.id %}">完了</a>
これで完了です。 尚、逆に消さないの選択肢を表示する場合は、 views.pyでコードする際に、item.done= Falseにしてあとは変数名をかえるだけで他は同じです。
10. フロントページからモデルをエディット修正
1. urls.pyに追記
アプリのurls.pyのurlpatternsにpathを追記します。これまで同様ですね。
path('edit/<list_id>', views.edit, name='edit')
2. views.pyに追記
関数を設定します。
#★ リクエストがPOSTなら、対象のIDのオブジェクトだけを変数itemに入れてね、そのitemでインスタンスを生成してListFormにいれてねの意味です。
#★★ もしフォームの内容が問題なければセーブして、メッセージを表示し、リダイレクトしてねの意味です。
#★★★ それ(POST)以外は、対象のIDのオブジェクトだけを変数itemに入れて、レンダーしてねの意味です。
def edit(request, list_id): if request.method == 'POST': #★ item = List.objects.get(pk=list_id) form = ListForm(request.POST or None, instance=item) if form.is_valid(): #★★ form.save() messages.success(request, ('リストを修正しました')) return redirect('todo:home') else: #★★★ item = List.objects.get(pk=list_id) return render(request, 'todo/edit.html', {'item': item})
3. テンプレートファイルの作成
templates > todo 直下にテンプレートファイルとしてedit.htmlを作成し、次のようにタイプします。
#★ これは、編集した際にデフォルトでFalseになる設定をしていた場合に、整合性を取るために必要です。
{% if item %} {{ item.item }} <form class="form-inline my-2 my-lg-0" method="POST"> {% csrf_token %} <input class="form-control mr-sm-2" type="search" placeholder="{{ item.item }}" value="{{ item.item }}" aria-label="Edit" name="item"> <input type="hidden" value="{{ item.done }}" name="done"> #★ <button class="btn btn-outline-secondary my-2 my-sm-0" type="submit">修正する</button> </form> {% endif %}