【django】todoリストアプリを作ってみる②(作り込む)【pythonメモ】

pythonLOGO

ブートストラップ等を導入して見た目を整えていく

urls.py

from django.urls import path
from .views import TodoList

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

views.py

from django.shortcuts import render
from django.views.generic import ListView, DetailView
from .models import TodoModel

# Create your views here.

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

class TodoDetail(DetailView):
  template_name = 'detail.html'
  model = TodoModel

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()),
]

<ink:pk>はobjectに格納されているプライマリーキー(pk)を取得するタグ。intはiteger型という事を指す

template下にlist.htmlを作成する

{{ object.title }}
{{ object.memo }}

起動してみる

python3 manage.py runserver
http://127.0.0.1:8000/detail/1
http://127.0.0.1:8000/detail/3

うまくいきました。

Bootstrapを導入する

ブートストラップのスターターテンプレートを引っ張ってくる

https://getbootstrap.com/docs/4.3/getting-started/introduction/

list.htmlにテンプレートをコピペする

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

    <title>Hello, world!</title>
  </head>
  <body>

    {% for item in object_list %} 

    <ul>
      <li>{{ item.title }}</li>
      <li>{{ item.memo }}</li>
    </ul>
    {% endfor%}
    
    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
  </body>
</html>

※いままで書いていたコードをbodyタグの中に入れる

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, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

    <title>Hello, world!</title>
  </head>
  <body>

    {{ object.title }}
    {{ object.memo }}

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
  </body>
</html>

Bootstrapのリファレンスを確認する

コンポーネントユーティリティ

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, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

    <title>Hello, world!</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 -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
  </body>
</html>

http://127.0.0.1:8000/detail/1を確認

うまくいきました。

htmlファイルの重複項目(Bootstrapスターターテンプレートやレイアウト等)をまとめる:base.html

templates下にbase.htmlを作成する

{% block header %}
{% endblock header %}

list.htmlを更新する

{% extends 'base.html' %}
{% comment %} このファイルはbase.htmlを基準として作成するという宣言 {% endcomment %}

{% block header%}
これがヘッダーです
{% endblock %}

さらにbase.htmlを更新=Bootstrapスターターテンプレートをコピペして、bodyタグ内に追記する

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

    <title>Hello, world!</title>
  </head>
  <body>
{% block header %}
{% endblock header %}

{% block content %}
{% endblock content %}

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
  </body>
</html>

list.htmlをさらに更新する

{% extends 'base.html' %}
{% comment %} このファイルはbase.htmlを基準として作成するという宣言 {% endcomment %}

{% block header%}
リストです。
{% endblock %}

{% block content %}
{% for item in object list %}
{{ item.title }}
{{ item.memo }}
{% endfor %}
{% endblock content %}

list.htmlをにBootstrapを適用してみる

Bootstrapコンポーネントからコピペ

{% extends 'base.html' %}
{% comment %} このファイルはbase.htmlを基準として作成するという宣言 {% endcomment %}

{% 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 content %}

サーバーを起動して確かめる

python3 manage.py runserver

うまくいきました。

本番環境を見据えたスタイルを作り込む

目標はこんな感じ

まずはタイトル部分から

ジャンボトロンを使う

{% extends 'base.html' %}
{% comment %} このファイルはbase.htmlを基準として作成するという宣言 {% endcomment %}

{% block header%}
<div class="jumbotron jumbotron-fluid">
  <div class="container">
    <h1 class="display-4">Todolist</h1>
    <p class="lead">TodoListを作って毎日を効率的に過ごしましょう。</p>
  </div>
</div>
{% 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 content %}

runserverで確認すると

いい感じ

list.htmlをいじる

list.htmlを更に更新。リストをコンテナ化する

{% extends 'base.html' %}
{% comment %} このファイルはbase.htmlを基準として作成するという宣言 {% endcomment %}

{% block header%}
<div class="jumbotron jumbotron-fluid">
  <div class="container">
    <h1 class="display-4">Todolist</h1>
    <p class="lead">TodoListを作って毎日を効率的に過ごしましょう。</p>
  </div>
</div>
{% endblock %}

{% block content %}

<div class='container'>

{% 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 %}

</div>

{% endblock content %}

list.htmlを更に更新。コンテナにボタンを配置する

※djangoで開発をする際は、ボタンタグではなく、aタグで生成されたボタンが良い

{% extends 'base.html' %}
{% comment %} このファイルはbase.htmlを基準として作成するという宣言 {% endcomment %}

{% block header%}
<div class="jumbotron jumbotron-fluid">
  <div class="container">
    <h1 class="display-4">Todolist</h1>
    <p class="lead">TodoListを作って毎日を効率的に過ごしましょう。</p>
  </div>
</div>
{% endblock %}

{% block content %}

<div class='container'>

{% for item in object_list %}

<div class="alert alert-secondary" role="alert">
  <p>{{ item.title }}</p>
  <a href="#" class="btn btn-info" tabindex="-1" role="button" aria-disabled="true">編集</a>
  <a href="#" class="btn btn-primary" tabindex="-1" role="button" aria-disabled="true">削除</a>
  <a href="#" class="btn btn-success" tabindex="-1" role="button" aria-disabled="true">詳細</a>
</div>
{% comment %} memoは、リンク先にするので削除した {% endcomment %}

{% endfor %}

</div>

{% endblock content %}

完成形に近づいてきました。

タスクの属性に応じてコンテナの色が変わるようにします

{% extends 'base.html' %}
{% comment %} このファイルはbase.htmlを基準として作成するという宣言 {% endcomment %}

{% block header%}
<div class="jumbotron jumbotron-fluid">
  <div class="container">
    <h1 class="display-4">Todolist</h1>
    <p class="lead">TodoListを作って毎日を効率的に過ごしましょう。</p>
  </div>
</div>
{% endblock %}

{% block content %}

<div class='container'>

{% for item in object_list %}

<div class="alert alert-{{ item.priority }}" role="alert">
  <p>{{ item.title }}</p>
  <a href="#" class="btn btn-info" tabindex="-1" role="button" aria-disabled="true">編集</a>
  <a href="#" class="btn btn-primary" tabindex="-1" role="button" aria-disabled="true">削除</a>
  <a href="#" class="btn btn-success" tabindex="-1" role="button" aria-disabled="true">詳細</a>
</div>
{% comment %} memoは、リンク先にするので削除した {% endcomment %}

{% endfor %}

</div>

{% endblock content %}

19行目の{{ item.priority }}を変数として使います。

item.priorityの設定をmodels.pyに仕込む

その他に期日=duedateも仕込む

from django.db import models

# Create your models here.
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()
  def __str__(self):
    return self.title

models.pyをいじったのでまたmakemygrationする

python3 manage.py makemigrations

すると何か出てくる

あらたにduedateという概念が出てきたけど、今までのデータに入っていないのでnullになるよ!だからmigrationしないよ!

という意味で、duedate = model.DateField(null = true)とする事もできるが、デフォルトでnullを許容しないようになっているので、そうなっている

で、聞かれているのは

1.デフォルトの数字を入れる
2.作り直す

今回は1を選択して1つずつ数字を入れる事にする。

timezone.now

と入れると今の時間を入れるよ!と言ってきているのでそれに従う

PRIORITYについてどうするかと聞かれているので、とりあえず全部’danger’という事にする

すると新たに

todo/migrations/0002_auto_20200719_1521.py

というファイルが作られる。

中身を見てみると

# Generated by Django 3.0.8 on 2020-07-19 15:21

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


class Migration(migrations.Migration):

    dependencies = [
        ('todo', '0001_initial'),
    ]

    operations = [
        migrations.AddField(
            model_name='todomodel',
            name='duedate',
            field=models.DateField(default=django.utils.timezone.now),
            preserve_default=False,
        ),
        migrations.AddField(
            model_name='todomodel',
            name='priority',
            field=models.CharField(choices=[('danger', 'high'), ('info', 'normal'), ('success', 'low')], default='danger', max_length=50),
            preserve_default=False,
        ),
    ]

AddFieldとしてduedateが追加されている。

で、あらためてmigrateする

python3 manage.py migrate

めでたくデータベースへの反映も完了した

/adminの中身の確認をしてみる

python3 manage.py runserver
http://localhost:8000/admin/todo/todomodel/3/change/

このようにPriorityとDuedateが追加されている

HTMLも確認する

http://localhost:8000/list/

一個一個のデータが反映されている事が確認できる



 

あ、宜しければ・・・。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です