【django初心者】1日でTODOアプリ開発【CRUDの基礎がこれだけで丸わかり】|備忘録

PROGRAMMING

この記事はただTODOアプリを作るだけではなく、初めてdjangoを触る方向けやコードを打っていても何しているか理解はしていないという方向けに作成しています。

コードを書くことによって、実際に何が起こっていて、その意味やdjangoで使われる概念をパワーポイントスライドを用いて説明していきます。

なのでdjango初心者には全体像を掴む上で、読むだけでも価値のある勉強になるかなと思います。

アプリケーション開発の初期設定

todoproject
 ├ config
 │ ├ __init__.py
 │    ├ asgi.py
 │    ├ settings.py
 │    ├ urls.py
 │    └wsgi.py
 ├ manage.py 
 ├ (pipfile) 
 └ (pipfile.lock)

まずはこの状態へ

今回、仮想環境はpipenvで構築します。(開発環境構築はそれぞれでお願いします。)

pipenv install
pipenv shell
pipenv install django

todoproject フォルダ内で以下のコードを打ち、プロジェクトを開始します。

django-admin startproject config .

最後の点(ドット)は『このフォルダ内に』の意味です。

ドットを付けないと、もうひとつ余分にフォルダを作成してしまいます。(目的のディレクトリでcodeを打つとき)

アプリを作る、urlの繋ぎこみ

次にアプリケーションを作成します。アプリを開始するときは、以下のコマンドを打ちます。

python manage.py startapp todo

ついでに、urls.pyを作成します(初期状態では、アプリにはurls.pyは準備されていない)

config/settings.py をいじっていく

まずは、djangoとアプリを繋ぎます。INSTALLED_APPSに ‘todo’,を追加

最後にコンマを打ち忘れましたが、(自分は)忘れがちなのであえて残しています。気をつけましょう

次に、TEMPLATES=[] です。テンプレートのディレクトリに関しては自由ですが、今回はBASE_DIRに指定していきます。

'DIRS': [],     
↓  
 'DIRS': [BASE_DIR, 'templates'],

BASE _DIRに変更したので、templatesフォルダをプロジェクト直下に作成しておきます。

config/urls.pyで todoアプリのURL繋ぐ(djangoに教える)

urlpatterns=[] に以下を追加します。

path(' ', include('todo.urls'))
※includeを使うときはimportを忘れないで!


以上でconfigの初期設定は終了です。(time_zoneや言語を変えたければ、settings.pyで)

todoアプリの設定に移りましょう!

アプリ内のurl とviewの繋ぎ込み

todoというview関数を作ることを前提として、urls.pyに以下のコードを書き込みます。(.viewsのドットもこのフォルダ内のviews.pyの意味)

次に、importしたviews.pyのtodo関数をviews.pyに書きます。(とりあえず、何も表示させないことをレスポンスとして返しています。)

※これらの作業はエラーを避けるための仮置きです。

models.py の設定

models.py では、Model=サイトを構成するデータソース(主にデータベース)へのアクセスを請け負います。 データを扱う設計書のようなものがModelになります。

そもそもデータベースって?

一言でいうと、図書館のようなものです。欲しいと思った本(データ)がどの棚のどの場所にあるかすぐに見つけられるシステムです。

左側、本が積み上げられているだけでは、読みたい本はすぐに見つけられません。これを見つけられるようにするための設計図=Modelを実際にみていきましょう。

データベース1つ1つには表(table)が入っています。Excelシートや、スプレッドシートのイメージです。

その表にどう言ったデータなのか指示を出すこと=データベースの設計図=Modelです。

どういったデータなのか指示を出す

todo/ models.pyを実際に記述する

modelはclassで記述していきます。よってキャメルケースを用いて頭文字と単語の切れ目は大文字にします。

また、引数は(models.Model)が基本で、modelsに関しては初期状態でimportされています。

ここは決まり文句みたいなものなので、書いていれば慣れてくるものだと思います。

classの中身ですが、どんなものが必要かをまず考えて、名前を付けます。とりあえず今回、todoのタイトル(title)とその内容 (memo) を指定します。(もちろん名前は何でもOK)

そうしたら、それぞれの名前に対して型を指定します。

Fieldの種類は以下のdjangoリファレンスの『Field types』を参照

https://docs.djangoproject.com/en/3.0/ref/models/fields/

makemigrationsとmigrate

今作ったデータベースの設計図(Model)を元に、実際にデータベースの中にテーブルを作っていきます。これがmigrationと言われる作業です。

イメージはdjangoが、pythonという言語をデータベースの言語に変換してくれている感じです。(これを自動でやってくれるところがフレームワークの凄さでもあります)

migrationの作業(コマンド)は2段階で行われます。
makemigrations と migrateです。

makemigrationsでは、models.pyの設計図を元に、もう少し詳しい設計図を作ります。models.pyファイルが更新され、makemigrationsというコマンドが打たれるたびに、0001、0002…といった具合に新たにファイルを作っていきます。(過去のデータベースの状態を参照できる)

また、データベースにデータを書き込む前にエラーがないかチェックする役割もあります。

migrateでは、makemigrationsで作成したファイルを元に、実際にデータベースにテーブルを作成するコマンドになります。

migrationのコマンドを打つ

『modelが完成したら、マイグレーション』と覚えましょう。
マイグレーションには2つのコマンドが必要になります。

python manage.py makemigrations
python manage.py migrate

まず、ターミナルにmakemigrationsのコードを書くと以下のように出ると思います。(成功すれば)

実際にtodo/migrationsフォルダに内に0001_initial.pyができています。

その中身を見ると、先ほど指定したFieldが自動で書かれています

ここでひとつ注意するのがIDというField。これは、djangoがテーブルを作るときに自動で設定するデータごとの”通し番号”のことです。(これでデータを判別するというイメージ)

次に、migrateコマンドを打つと以下のようになります。何だかいろいろとテーブルが作成されているのがわかるかと思います。

意識して欲しいのは、一番最後のtodo.0001_initial …OKです。

今回意図して作ったテーブルは最後のtodo.0001_initialのテーブルのみです。残りのものはdjangoがデフォルトで準備しているもので、プロジェクトを作って1番最初にmigrateすると作られるものです。(2回目以降は作成されません。)

admin管理画面にアクセスし、データ入力

migrationすることで、データベースにテーブルを作成しました。このテーブルにデータを入れていく方法の中で簡単な方法として管理画面からの入力があります。

管理画面のURLはconfig/urls.pyにstartprojectをした時点で指定されています。(初期設定では ‘admin/’)

python manage.py runserverの後に、
http://127.0.0.1:8000/admin/ にアクセスすると、以下のような管理画面へのログインページが開かれます・

superuserの作成

ここにログインするためにスーパーユーザ(全ての管理者権限を持つユーザー)を作成します

コマンドは
python manage.py createsuperuser

Username:  任意(何も打たなければ自分の名前)
Email address:  開発用なので指定しなくても大丈夫
Password:  任意(開発用なので、testや1など簡単なものでOK)
Password (again):  同じものを入力

(パスワード認証の条件が満たされていないと、上記の画像赤文字のような警告が出ますが、ローカル環境でやることなので、とりあえず作りますか?y(YES)と答えます。)

再度、http://127.0.0.1:8000/admin/ にアクセス後、スーパーユーザーでログインすると、管理画面に入れます。

しかし、初期設定で存在するGroupsとUsersの項目しかありません。先ほど作ったtodoモデルを反映させるには、todo/admin.pyにコードを書く必要があります。

admin.site.registe(反映させたいモデル)
のように追記します。モデルimportを忘れないように

もう一度、管理画面にアクセスすると…

TODOアプリのTodo modelsが反映されています。addというボタンを押して実際にデータを入力していきます。

models.pyに記述したフィールドがちゃんと反映されていますね。データを入力してSAVEしていきます。

2つのデータを作りましたが、名前がわかりにくいのでmodels.pyにタイトル文字列を反映させるように設定していきます。

モデルclassにPythonの特殊メソッドを追加します。

class MyClass:
  def __str__(self):
    return ... # stringオブジェクト

__str__(): メソッドは上のような形をとり、stringオブジェクトを返します。classに関数を記述するときは必ず引数にselfを指定します。selfとはインスタンス(そのclass の実際のデータ(オブジェクト))を生成したときのインスタンスオブジェクト自身のことです。

よって、self.titleをreturnするとは、先ほど管理画面で作ったデータのtitleに書かれた文字列を返すということになります。先ほどの画面をリロードすると…

タイトルの文字列が表示されるようになりました!

CRUDとdjangoテンプレート

これからは、modelで作ったデータをHTMLファイルに埋め込むという作業をやっていきます。

CRUDとは、データベースを持つwebアプリケーションにおいて、実装が必要である主な機能の列挙です。

  • C Create(作成する)
  • R Read(読み込み)
  • U Update(更新する)
  • D Delete(削除する)

Twitterならば、ツイートする、ツイートを読む、ツイートを更新、ツイートを削除。といった具合ですね。

djangoでは、あらかじめCRUD機能を実装するためのテンプレートViewが準備されています。

djangoのタグ

CRUDの実装に入る前に、htmlテンプレートとdjangoのタグの埋め込みについて少しだけ解説します。

通常のHTMLタグに加えて、djangoでif文やfor文などの処理を行いたいときに、{% 処理 %}タグで囲みます。

また、データベースからデータを埋め込みたいときは、{{ データ }}タグで囲みます。

例)
{% if object.thumbnail %} 
<li >Your Image<img src="{{object.thumbnail.url }}" > </li> 
{% endif %}

画像があるならば、listの中で画像を表示してね。src はデータベースにあるobject.thumbnailのurlだよ。みたいな感じです↑
HTMLタグと{%%},{{}}タグが混在しているのを確認していただければOKです。

Read:ListView

ListViewは『データの一覧をリストとして表示すること』に便利な機能をたくさん持っているテンプレートです。

まずは、先ほど仮置きしたtodo/urls.py と todo/views.pyを書き換えます。

まずはurls.py。 urlの指定とそこで使うviewの繋ぎ込みです。ListViewを使うクラスをTodoListとしています。(まだ作ってない)

classを使うときは.as_view()です

次にviews.py。urls.pyで繋いだTodoListクラスを書いていきます。

modelとListViewのimportを忘れずに

引数にLIstViewを受け取ります。
CRUDのViewを使うときは、modelとtemplate_nameの指定がほとんどの場合行われます。

逆にこれだけやれば、最低限の機能を使えてしまうから便利なのです。たった2行でリストの形でデータの列挙ができてしまいます。

では、ここで指定したtemplateをプロジェクト直下に作ったtemplatesフォルダの中にlist.htmlという名前で作成しましょう!

list.htmlの中は、先ほど作ったモデルデータをfor文(ほぼPythonの内容ですが、djangoでは以下のように使います。)で列挙していきます。

データは初期では”object”として参照できます。また、ListViewで指定したモデルのリストは”object_list”という名前で参照できます。

これは、views.py のTodoListクラス内でcontext_object_name = ‘lists’などとして名前を変更することもできます。

object_listからitemというデータを持ってきて、そのtitleとmemoを表示してくださいというコードになります。

ブラウザでの表示

2個だけだと、ありがたみを感じにくいかもしれませんが、データが100個でも1万個でも同じコードで列挙できてしまいます。1万個のデータをHTMLだけで書こうとしたら、PCのキーボードが先に壊れるんじゃないでしょうか。笑

Read:DetailView

DetailViewは『データの詳細を表示すること』に適したテンプレートです。ListViewと同じくCRUDの中のReadの役割を持ちます。よって、コードの書き方も近いです。

異なる点は、データをひとつひとつ表示するために通し番号(Primary Key)が必要なことです。

makemigrations をしたときにできたtodo/migrations/0001_initial.pyの中でidというFieldがありました↓

primary_keyの場所

この中で、primary_key=Trueとなっています。todoモデルのテーブルの中のデータにはそれぞれ番号がついているということになります。

DetailViewでは、このPrimary_Keyを利用することにより、個別のデータへとアクセスします。逆に、Primary_Keyを指定してあげないと、djangoがどのデータをブラウザに表示させるかわからなくなってしまい、エラーを吐き出すので注意が必要です。

では、まずurls.pyにDetailViewをつなげましょう。

<int:pk>に注目 pk=Primary_Key

TodoDetailという名前のクラスを用意するとして、importとパスをつなげます。ここで、URLにPrimary_Keyを設定してあげることに注意です。

※ここでPrimary_Keyを忘れるとエラーになります。

intとは整数型のことです。『http://127.0.0.1:8000/detail/1/』のようにリクエストされた場合にDetailViewでレスポンスするという形になります。

次はviews.pyです。これはListVeiwと同じですね、importを忘れないようにしましょう。

template_nameをdetail.htmlに指定したのでプロジェクト直下のtemplatesフォルダに、作っていきます。

今回は、データは一つですので、object.titleのような書き方になります。以上でDetailViewは終了です。

base.htmlの利用

2つのHTMLファイルが完成したので、少しCRUDから離れてbase.htmlの利用についてやっていきます。

通常のwebサイトでは、ヘッダーやフッターといったものは、どのページアクセスしても同様のものが使われることがよくあります。

これは見た目だけの話ではなく、実際のHTMLのコードでも同じです。同じものを何回もコピペをするのは効率が悪いため、djangoがwebサイトの基礎となるHTMLファイルを作り、それを継承する機能を準備してくれています。

通常base.htmlと命名されるが、名前はなんでもいい

まずはbase.htmlです。見た目を整えるために、Bootstrapを導入していますが、HTMLとCSSについては解説なしでいこうと思います。(base.htmlはBootstrapのHPからコピペ+blockタグで構成しています。)

djangoでは”blockタグ”によって、継承先の内容を変化させることができます。つまり、{% block %}〜{% endblock %}の間をテンプレートごとに変化させることで共通部分を維持したまま、様々なWebページが作成可能になります。

detail.htmlでは、まずbase.htmlの継承を行っています。これで、<html>タグや<head>タグなどを継承し、Bootstrapも有効になっています。よって、<body>タグの中身だけを記述すればOKになります。

blockタグの中身は以下の通りです。(Bootstrapのところは写経か無視でお願いします)

detail.html

続いて、list.html。こちらも同様です。ただし、item.memoはListViewでは、必要ないので削除しています。

modelデータとcssの連携

djangoでは、テンプレートに処理を埋め込むことによって、データごとに、見た目を変えることができます。

まずは、models.py の変更。priorityとduedateの追加をしています。

少し難しいですが、テンプレートに、alert-{{ item.priority }}とすることで、データごとに文字列を変化させています。

templates/list.html > block content

modelを作ったらマイグレーション」です。まずはmakemigrations

そうすると、すでにあるモデルにnullを許さない’duedate’フィールドを追加しなければならないと出てきます。

  1. 既存の数字を何か入れる
  2. モデルを作り直す

DateFiled(null=True)とすることで防げますが、今回は1を選択してtimezone.now(現在の時間)を入れていきます。

次は、priorityに関しても同じような質問がきています。とりあえず、’danger’, ‘info’, ‘success’の中から’danger’を入れます

間違えました。 ‘ ‘が必要ですね

makemigrationsが成功したためmigrateしていきましょう!

migrateも成功

CreateView

だいぶ、脱線しましたが、CRUDに戻ります。次は、CreateViewで作成ををするVIewです。

まずは、urls.py。ここは、ほとんど同じですね

次は、views.pyです。こちらもほぼいつも通りですが、formを作成するために、そのformにどんなfieldを使うのかを指定してあげる必要があります。

fields=(,,,)が必要

最後はテンプレートです。継承とブロックタグはいつもどおり

formタグについて
action: データを次にどこに送るか。空欄ならば、また同じところに戻ってくる
method: GETとPOSTがある。問合せフォームのようなものは一般的にはPOSTが使われる。

{{form.as_p}} djangoがCreateViewにより自動で作ってくれるフォームをpタグで囲うという意味になります。

{% csrf_token %} : djangoが準備してくれているセキュリティ対策のコードです。djangoでフォーム作成するときは必ず入れないとエラーになります。

inputタグ: 送信ボタンですね

では実際にformでTodoListを作成してみましょう!

『作成する』を押すと…

リダイレクト先を指定するかモデルでget_absolute_urlを指定しろと書いてあります。

エラーが出てしまいました。データ作成はできたけど、次にどのページを表示(redirect)すればいいかわからないとdjangoが言っています。

今回はmodelに対し、フォームを利用したときに飛ぶ場所を指定していく方法(sucess_url)でエラーを回避したいと思います。

コードsucces_urlの記述は、 views.py にします。

作成に成功したときのURLを指定するのですが、ViewからURLを指定するreverse関数を使います。

reverse関数には、urls.py/urlpatternsのpathで指定したnameを引数とします。つまり、nameによってどのURLに逆戻りするのかを指定します。

実は、nameはまだ指定していなかったので、urls.pyに戻ります。

このようにnameを追加してあげると、フォーム作成後、’list/'(ListViewのテンプレート)のURLにリダイレクトするようになりました。以上でCreateViewは終了です!

DeleteView

データを削除するときのViewになります。

urls.py。detailViewと同様にPrimary_Keyを指定してあげましょう。(削除するときにどのデータかdjangoに教えないとエラー)

.viewsからのimportを忘れずに

views.pyはCreateViewと同じですね。form形式での削除ですので succes_urlが必要になります

最後はテンプレート。{% csrf_token %}を忘れずに、『削除ボタン』だけ設置します。

UpdateView

データを更新するときのViewになります。ほぼ同じです

urls.py

views.py はcreateViewとほとんど同じになります。fields=()に制限をかけて全てを変更できないようにしたりすることもできます。

views.py
update.html

URLタグの設定

まずは、list.htmlのhref属性がダミーになっているのでdjangoのコードに書き換えていきましょう

{% url ‘name’ %}の形をとります

しかし、ここでlistViewを見にいくと、エラーが出てしまいました。これは、url指定が個別にされてないよとdjangoが言っています。

つまり、何番目のデータについて編集や削除をするのか教えてあげないといけません。ここでは、item.pk(itemはfor文で回しているデータ)でそれぞれのデータに対してprimarykeyを呼び出し、urlに渡してあげます。

item.pkを追加

これにて、TODOLISTアプリは完成です!お疲れ様でした!