DjangoでTodoアプリを作成その１

pycharmでプロジェクトを作成する。
![スクリーンショット 2021-03-22 12.18.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1226385/99fb493d-953e-72f3-a40f-ee4634275f0d.png)

satrt appコマンドでtodoアプリを作成する。
![スクリーンショット 2021-03-22 12.19.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1226385/ad2ab359-b0be-59ec-9dfb-05f7ebabd74a.png)

todoアプリを追加する。

```setteings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'todo',
]
```

templatesフォルダが作成されていることを確認する。

todoフォルダ以下にurls.pyファイルを作成する。

#####urlのつなぎこみ

まずはtodoprojectのurls.pyの編集。includeを使って、
アプリにurls.pyにつなげる。

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


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

######models.pyの編集
CharFieldはタイトルなどの短めの文字のための型。
TextFieldは長めの文字のための型


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

# Create your models here.

class TodoModel(models.Model):
    title = models.CharField(max_length=100)
    memo = models.TextField()
```

##### makemigrationsとmigrate
makemigrationsはmodels.pyとDBの中間にある生成物。
過去のDBに戻すことができる。

migrateは実際にDBにテーブルができあがる。

##### makemigrationsの実行

```
manage.py@todoproject > makemigrations
Migrations for 'todo':
  todo/migrations/0001_initial.py
    - Create model TodoModel
```

##### migrateの実行
 
```
manage.py@todoproject > migrate
```

##### createsuperuser

```
manage.py@todoproject > createsuperuser
```

##### admin.pyの編集　
管理画面から直接DBにデータをセットできるようにする。

```todo/admin.py
from django.contrib import admin
from .models import TodoModel

# Register your models here.

admin.site.register(TodoModel)
```

##### 管理画面のデータ一覧画面の表示を変更
タイトルが表示されるようにする。

```todo/admin.py
class TodoModel(models.Model):
    title = models.CharField(max_length=100)
    memo = models.TextField()
    def __str__(self):
        return self.title
```

#### djangoのテンプレートとCRUD（クラッド）

C Create(作成する）
R Read(読み込む、情報を取る）
U Update(更新する）
D Delete(削除する）

#### CRUDとDjangoのテンプレート

C Create(作成する）→　CreateView
R Read(読み込む、情報を取る）→　ListView,DetailView
U Update(更新する）→　UpdateView
D Delete(削除する）→　DeleteView

#### ListView

データの一覧をリストとして表示することに適したテンプレート

##### ListViewの作成

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

urlpatterns = [
    path('list/', TodoList.as_view()),
]
```

modelでmodels.pyにあるどのmodelを使用するかを指示する。

```todo/views.py
from django.shortcuts import render
from django.views.generic import ListView
from .models import TodoModel

# Create your views here.

class TodoList(ListView):
    template_name = 'list.html'
    model = TodoModel
```

object_listはDBのオブジェクトが入っている。

```list.html
{% for post in object_list %}
    {{ post.title }}
    {{ post.memo }}
{% endfor %}
```

#### DatailView

データの中身を表示することに適したテンプレート
<int:pk>とはinteger型のプライマリーキーのこと。
詳細ページを表示する際に何のデータを表示するかを指定する必要があるため。

```todo/urls.py
from django.urls import path
from .views import TodoList, TodoDetail

urlpatterns = [
    path('list/', TodoList.as_view()),
    path('detail/<int:pk>', TodoDetail.as_view()),
]
```


```todo/views.py
class TodoDetail(DetailView):
    template_name = 'detail.html'
    model = TodoModel
```

templatesディレクトリ配下にdetail.htmlを作成

detailページは詳細画面なので多数のデータでなく、一つのデータ
を対象としているので、object_listではなく、objectを利用する。

```detail.html
{{ object.title }}
{{ object.memo }}
```


#### bootstrapで見た目をきれいにする
[bootstrap](https://getbootstrap.jp/)

ドキュメントのスターターキットをコピーしてtemplateに貼り付けて、
bodyタグに中身を書く。

```templates/detail.html
<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">

    <title>私は仕事が大好きです。</title>
  </head>
  <body>

  <div class="alert alert-primary" role="alert">
    {{ object.title }}
  </div>
  <div class="alert alert-primary" role="alert">
    {{ object.memo }}
  </div>

    <!-- Optional JavaScript; choose one of the two! -->

    <!-- Option 1: Bootstrap Bundle with Popper -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>

    <!-- Option 2: Separate Popper and Bootstrap JS -->
    <!--
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/dist/umd/popper.min.js" integrity="sha384-q2kxQ16AaE6UbzuKqyBE9/u/KzioAlnx2maXQHiDX9d4/zp8Ok3f+M7DPm+Ib6IU" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.min.js" integrity="sha384-pQQkAEnwaBkjpqZ8RU1fF1AKtTcHJwFl3pblpTlHXybJjHpMYo79HY3hIi4NKxyj" crossorigin="anonymous"></script>
    -->
  </body>
</html>
```

#### base.htmlを使用する。


```base.html
<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">

    <title>私は仕事が大好きです。</title>
  </head>
  <body>

  {% block header %}
  {% endblock header %}
  
  {% block content %}
  {% endblock content %} 
  
    <!-- Optional JavaScript; choose one of the two! -->

    <!-- Option 1: Bootstrap Bundle with Popper -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>

    <!-- Option 2: Separate Popper and Bootstrap JS -->
    <!--
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/dist/umd/popper.min.js" integrity="sha384-q2kxQ16AaE6UbzuKqyBE9/u/KzioAlnx2maXQHiDX9d4/zp8Ok3f+M7DPm+Ib6IU" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.min.js" integrity="sha384-pQQkAEnwaBkjpqZ8RU1fF1AKtTcHJwFl3pblpTlHXybJjHpMYo79HY3hIi4NKxyj" crossorigin="anonymous"></script>
    -->
  </body>
</html>
```



```list.html
{%  extends 'base.html' %}
{% block header %}
リストです。
{% endblock %}

{% block content %}
    {% for item in object_list %}
        <div class="alert alert-secondary" role="alert">
        {{ item.title }}
        </div>
        <div class="alert alert-secondary" role="alert">
        {{ item.memo }}
        </div>
    {% endfor %}
{% endblock %}
```

#### 一覧画面の見た目を整える

```list.html
{%  extends 'base.html' %}
{% block header %}
<div class="container">
  <div class="bg-light p-3 p-sm-5 my-4 rounded">
    <h1 class="display-4">Todolist</h1>
    <p class="lead">Todolistを作って毎日を効率的に過ごしましょう。</p>
</div>
{% endblock %}

{% block content %}
    <div class='container'>
    {% for item in object_list %}
        <div class="alert alert-secondary" role="alert">
        <p>{{ item.title }}</p>
        <a class="btn btn-info" href="#" role="button">編集</a>
        <a class="btn btn-info" href="#" role="button">削除</a>
        <a class="btn btn-info" href="#" role="button">詳細</a>
        </div>
    {% endfor %}
    </div>
{% endblock %}
```

alert-のあとに変数が入るように変更。

```list.html
        <div class="alert alert-{{ item.priority }}" role="alert">
```

models.pyに以下を追加する。
PRIORITYはタプル型。左側は上記の変数に入る値。右側は画面で選択する際に表示される値。

```models.py

PRIORITY = (('danger', 'high'), ('info', 'normal'), ('success', 'low'))

class TodoModel(models.Model):
    title = models.CharField(max_length=100)
    memo = models.TextField()
    priority = models.CharField(
        max_length=50,
        choices=PRIORITY
    )
    duedate = models.DateField()
```

models.pyを変更したため、makemigrateを行う。
以下のように入力を求められる。
これは、「すでにあるデータのduedateの中身をどうするか？
デフォルトでnullを入力できません。」という内容。
１→何かしらの数字をセットする。
２→出直す。

```
manage.py@todoproject > makemigrations
You are trying to add a non-nullable field 'duedate' to todomodel without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option:
```

1を選ぶ。
どんな数字を入れますかと聞かれる。

```
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
```

timezone.nowを入力

priorityについても同じように聞かれる。
1を入力
'danger'を入力

```
You are trying to add a non-nullable field 'priority' to todomodel without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option:  1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>>  'danger'
```

migrateを実行

```
manage.py@todoproject > migrate
```