LoginSignup
5
7

More than 3 years have passed since last update.

DjangoでTodoアプリを作る②フォルダ一覧ページの作成

Last updated at Posted at 2020-04-17

このチャプターでは、フォルダ一覧ページを作成していきます!

記事一覧

DjangoでTodoアプリを作る①Dockerで環境を構築する
DjangoでTodoアプリを作る②フォルダ一覧ページの作成
DjangoでTodoアプリを作る③タスク一覧ページの作成
DjangoでTodoアプリを作る④フォルダー、タスク作成機能の実装
DjangoでTodoアプリを作る⑤タスク編集機能の作成

URLの設定

フォルダー一覧ページの設計は以下の通り。

URL 処理
/foleders/{フォルダのID}/tasks フォルダー一覧ページを表示する

この設計を踏まえて、django_todo/urls.pytodo/urls.pyを編集していきます。

django_todo/urls.py

まず、include関数を使いたいので、from django.urls…の行を変更し、インポートを追加します。そして、todo.urls をインポートする行を追加します。結果、django_todo/urls.pyは以下のようになります。

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

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

urlが'folders/'だった場合は、todo/urls.pyを参照しなされという意味です。
そのtodo/urls.pyは次のように定義します。

todo/urls.py

todo ディレクトリの下に、新しく urls.py という空のファイルを作ります。

$ touch todo/urls.py

作成した、todo/urls.pyを以下のように編集します。

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

urlpatterns = [
    path('<int:id>/tasks', views.index, name='tasks.index'),
]

最初の2行は、Djangoの path 関数と、todoの全ての ビュー(今は1つもありません。これから作っていきます!)をインポートするという意味です。

アドレスの文字列の部分にある< int:id >が引数の型と名称を指定しています。その結果、この部分に現れるint型の値が、idという名前で取得され,対応するview関数(ここでは、view.pyのindex関数)に引数として渡されます。

urlpatterns = ...の部分において、folders/の下に< int:id >/tasksがある、つまり、folders/< 整数 >/tasksというアドレスが来ると、view.pyのindex関数を呼び出すという処理をします。

name=では、このURLパターンに名前をつけています。

モデルの定義

folderテーブルの定義は以下のようにします。

カラム論理名 カラム物理名
ID id
タイトル title
作成日 created_at

このテーブル定義をもとに、todo/models.pyを編集していきます。

todo/models.py
from django.conf import settings
from django.db import models
from django.utils import timezone


class Folder(models.Model):
    title = models.CharField(max_length=20)
    created_at = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.title

idについての記述がありませんが、Djangoでは自動的に定義されます。詳細は、モデル |Djangoドキュメントが詳しいです。
定義したフィールドの内容は以下の通りです。

フィールド どんなフィールドか
models.CharField 文字数が制限されたテキストを定義するフィールド
models.DateTimeField – 日付と時間のフィールド

__str__関数では、管理画面などで表示される文字列を定義しています。(models.pyに記述されたdef __str__(self)とは?)

view関数の定義

todo.pyにビューを記述していきます。
以下のように編集してください。

todo/views.py
from django.shortcuts import render
from django.utils import timezone
from .models import Folder

def index(request, id):
    folders = Folder.objects.filter(created_at__lte=timezone.now()).order_by('created_at')
    return render(request, 'index.html', {'folders':folders})

一行目でrender関数をインポートしています。
二行目では、models.pyで定義したFolderモデルをインポートしています。

render関数では、第一引数にrequest、第二引数にテンプレートファイル名を記述しています。
第3引数では、テンプレートに渡すデータについて記述しています。renderの第3引数に辞書を渡すことでtemplateに値を渡すことができます。
template内で渡された値を使う場合は{{ folders }}の形式で書きます。

※「render」は、「与える」、「差し出す」といった意味を持った英単語。requestやfoldersをindex.htmlに「差し出す」という意味で捉えるとわかりやすそう。

templateの作成

テンプレートとはアプリケーションがレスポンスする HTML の雛形で、制御構文(if や foreach など)や変数の展開を記述することができます。ページの枠組みだけ用意して、URL に応じて変わる箇所だけが穴埋めになっているイメージ。

以下のコマンドでtodoディレクトリにtemplatesディレクトリを作成し、その中にindex.htmlを作成します。

$ mkdir todo/templates
$ touch todo/templates/index.html

index.htmlは以下のように編集してください。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Todo</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootflat/2.0.4/css/bootflat.min.css">

</head>
<body>
<header>
  <nav class="my-navbar">
    <a class="my-navbar-brand" href="folders/1/tasks">Todo</a>
  </nav>
</header>
<main>
  <div class="container">
    <div class="row">
      <div class="col col-md-4">
        <nav class="panel panel-default">
          <div class="panel-heading">フォルダー</div>
          <div class="list-group">
            {% for folder in folders %}
                <a 
                href="{% url 'tasks.index' id=folder.id %}"
                class="list-group-item {% if current_folder_id == folder.id %}active{% endif %}"
                >
                    {{ folder.title }}
                </a>
            {% endfor %}
          </div>
          <div class="panel-body">
            <a href="#" class="btn btn-default btn-block">
              フォルダーを追加する
            </a>
          </div>
        </nav>
      </div>
      <div class="column col-md-8">
        <!-- タスクはここに表示される -->
      </div>
    </div>
  </div>
</main>
</body>
</html>

head部分についてはHTML5のhead内の記述が詳しいです。

上記のhtmlの記述の中で、注目すべきところは以下の部分。

index.html
{% for folder in folders %}
<a 
href="{% url 'tasks.index' id=folder.id %}",
class="list-group-item {% if current_folder_id == folder.id %}active{% endif %}"
>
  {{ folder.title }}
</a>
{% endfor %}

テンプレートの中では、{% %}で囲むことによってpythonのようにfor文を使用することができます。

href = ... では、todo/urls.pyの path() 関数の name 引数を定義していた(ここでは、tasks.index)ので、テンプレートタグの{%url%} を使用してリンク先を表しています。

class="list-group-item {% if current_folder_id == folder.id %}active{% endif %}"

この部分では、現在選択されているフォルダのIDをactiveに設定しています。

CSSの作成

まず、以下のようなディレクトリ構成になるようにtodoディレクトリ配下にstaticディレクトリ、cssディレクトリ、style.cssファイルを作成します。

todo
├── static
│   └── css
        └── style.css

style.cssは以下のように記述します。

style.css
body {
    background-color: #b4e7cf;
  }

  .navbar {
    margin: 2rem 0 2.5rem 0;
  }

  .my-navbar {
    align-items: center;
    background: #474a2c;
    display: flex;
    height: 6rem;
    justify-content: space-between;
    padding: 0 2%;
    margin-bottom: 3rem;
  }

  .my-navbar-brand {
    font-size: 20px;
  }

  .my-navbar-brand,
  .my-navbar-item {
    color: #b4e7cf;
  }

  .my-navbar-brand:hover,
  a.my-navbar-item:hover {
    color: #ffffff;
  }

  .panel-default>.panel-heading {
    color: #474a2c;
    background-color: #9bdeac;
    border-color: #e6e9ed;
}

a.list-group-item:hover {
  color: #fff;
  background-color: #474a;
}

a.list-group-item.active,
a.list-group-item.active:focus,
a.list-group-item.active:hover{
  color: #fff;
  background-color: #474a;
  border-color: #e6e9ed;
}

a {
  color: #474a;
  text-decoration: none;
}

a:hover{
  color: #4909;
}

.btn-default:hover {
  color: #fff;
  background-color: #474a;
}

.btn{
  border-color: #474a;
}

.btn-primary {
  background-color: #474a;
  border-color: #474a;
}

.btn-primary:hover {
  background-color: #4909;
}

  .table td:nth-child(2),
  .table td:nth-child(3),
  .table td:nth-child(4) {
    white-space: nowrap;
    width: 1px;
  }

  .form-control[disabled],
  .form-control[readonly] {
    background-color: #fff;
  }

index.htmlにCSSを読み込ませるため、index.htmlを編集します。
1行目に以下を追加してください。

index.html
{% load static %}

head部分に以下を追加してください。

index.html
<link rel="stylesheet" href="{% static 'css/style.css' %}">

マイグレーション

次のコマンドでマイグレーションファイルを作成します。以下のようになればOKです。

$ docker-compose run web python3 manage.py makemigrations
Starting django_todo_db_1 ... done
Migrations for 'todo':
  todo/migrations/0001_initial.py
    - Create model Folder

コマンド実行後、以下のようなマイグレーションファイルが作成されます。

todo/migrations/0001_initial.py
# Generated by Django 2.2.12 on 2020-04-16 22:22

from django.db import migrations, models
import django.utils.timezone


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='Folder',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('title', models.CharField(max_length=20)),
                ('created_at', models.DateTimeField(default=django.utils.timezone.now)),
            ],
        ),
    ]

このファイルをマイグレートすることによって、データベースを作成することができます。

$ docker-compose run web python3 manage.py migrate       
Starting django_todo_db_1 ... done
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, todo
Running migrations:
  Applying todo.0001_initial... OK

adminページの作成

作成したFolderクラスにおいて追加、編集、削除するのにDjango adminを使います。
todo/admin.pyファイルを、内容を次のように編集してください。

todo/admin.py
from django.contrib import admin
from .models import Folder

admin.site.register(Folder)

先ほど定義したFolderモデルをimportしています。 また、モデルをAdminページ(管理画面)上で見えるようにするため、admin.site.register(Folder)でモデルを登録しています。
そして、http://localhost:8000/admin/ にアクセスしてみてください。 
次のようなログインページが表示されます。
Screenshot from 2020-03-17 16-41-41.png
ログインするには、superuser (サイトの全てを管理するユーザー)を作る必要があります。
以下のコマンドでsuperuserを作成します。

$ docker-compose run web python3 manage.py createsuperuser

ユーザーネーム、メールアドレス、パスワードを入力していき、

Superuser created successfully.

となればユーザーの作成が完了です。
adminページに戻り、ログインすると以下のようなページが表示されます。

Screenshot from 2020-04-17 07-29-46.png

Foldersの、「追加」をクリックすると次のページが出てくるはずです。

Screenshot from 2020-04-17 07-31-18.png

なんでもよいので、2,3個folderを追加しておきましょう。ぼくは、「プログラミング」、「大学の課題」、「その他」を追加しておきました:v:
次のコマンドでデータが挿入されていることが確認できます。詳しくはDjango ORM(クエリセット) ・Django Girls Tutorial

$ docker-compose run web python3 manage.py shell
Starting django_todo_db_1 ... done
Python 3.8.2 (default, Feb 26 2020, 14:58:38) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from todo.models import Folder
>>> Folder.objects.all()
<QuerySet [<Folder: プログラミング>, <Folder: 大学の課題>, <Folder: その他>]>

確認

$ docker-compose up -dでサーバーを立ち上げて、http://localhost:8000/folders/1/tasks にアクセスしてみましょう。
以下のようになっていたらOKです!
Screenshot from 2020-04-17 09-22-00.png

おわりに

これでこのチャプターは終わりです!
ここまでのコードは、リポジトリのchapter2ブランチにあります。
次のチャプターではタスク一覧ページを実装していきます!
DjangoでTodoアプリを作る③タスク一覧ページの作成

記事一覧

DjangoでTodoアプリを作る①Dockerで環境を構築する
DjangoでTodoアプリを作る②フォルダ一覧ページの作成
DjangoでTodoアプリを作る③タスク一覧ページの作成
DjangoでTodoアプリを作る④フォルダー、タスク作成機能の実装
DjangoでTodoアプリを作る⑤タスク編集機能の作成

参考文献

Djangoで外部キー制約を記述する

5
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7