初回操作
$ mkdir test $ cd test $ python3 -m venv venv $ source venv/bin/activate $ pip install django==3.2
なお次から開発するときも毎回 $ source venv/bin/activate だけはコマンドする必要がある
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('', include('book.urls')), ]
test/bookproject5/bookproject/urls.py を書く
from django.urls import path, include を追加して include ライブラリを使えるようにする
path('', include('book.urls')), 追加
第一引数が空文字なので http://127.0.0.1:8000/ でURLマッチングして book アプリケーションの urls.py を呼び出す
なお 127.0.0.1 はローカルループバックアドレスで自分自身を指す
django はプロジェクトの urls.py でドメインURLを受け取り、各アプリの urls.py で更に深いディレクトリへの urlpatterns を定義するのが定跡らしい
参考 https://office54.net/python/django/urls-path-include
通常操作
from django.db import models class SampleModel(models.Model): title = models.CharField(max_length=100) number = models.IntegerField()
test/bookproject5/bookproject/book/models.py を書く
django は DB を表で管理し、モデルと呼ぶ
( DB 設計図は C++ の構造体みたいな感じで書く)
DB設計図の作成
(venv)$ python3 manage.py makemigrations
上記コマンドを打つと
Migrations for 'book':
book/migrations/0001_initial.py
- Create model SampleModel
と表示されて sqlite3 設計図が test/bookproject5/bookproject/book/migrations/ に 0001_initial.py として作成される。
(なお models.py を追記して makemigrations コマンドを実行する毎に 0002_, 0003_, ... が作成される)
(venv)$ python3 manage.py migrate
上記コマンドで sqlite3 設計図に従って実際にモデル(データベーステーブル)が作成される
( makemigrations で作成された設計図を基に sqlite3 が DB table を実際に作成するコマンド)
(venv)$ python3 manage.py createsuperuser Username (leave blank to use 'takahiro'): tkr987 Email address: Password: Password (again): Superuser created successfully.
上記コマンドで superuser を作成(Emailは空白でok)
from django.contrib import admin from .models import SampleModel admin.site.register(SampleModel)
test/bookproject5/bookproject/book/admin.py を書く
admin.py に model を追加することで、管理画面にDB設計図が反映され管理できるようになる
サーバーを立ち上げて http://127.0.0.1:8000/admin/book/ にアクセス
CRUD (Create Read Update Delete)
- CreateView
- ListView / DetailView
- UpdateView
- DeleteView
ListView(一覧画面の作成)
from django.urls import path from . import views urlpatterns = [ path('book/', views.ListBookView.as_view()), ]
test/bookproject5/bookproject/book/urls.py に上記を書く
パス(url)に'book/'という文字が入っていると views.py ファイルの中の Listview を定義した ListBookView の view を呼び出す
from django.shortcuts import render from django.views.generic import ListView from .models import Book class ListBookView(ListView): template_name = 'book/book_list.html' model = Book
test/bookproject5/bookproject/book/views.py に上記を書く
基底 ListView を class ListBookView が継承する形で作っていく
from sre_constants import CATEGORY from unicodedata import category from django.db import models CATEGORY = (('business', 'ビジネス'), ('life', '生活'), ('other', 'その他')) class Book(models.Model): title = models.CharField(max_length = 100) text = models.TextField() category = models.CharField(max_length = 100, choices = CATEGORY) def __str__(self): return self.title
test/bookproject5/bookproject/book/models.py に上記を書く
choices は右辺で定義した tuple をプルダウンで表示するオプション
CATEGORY はコード上で識別するIDと表示を組にして定義する組み込み変数 (sre_constants)
(venv)$ python3 manage.py makemigrations (venv)$ python3 manage.py migrate
models.py を修正したので上記コマンドで反映させる
from django.contrib import admin from .models import Book admin.site.register(Book)
test/bookproject5/bookproject/book/admin.py に上記を書く
サーバーを立ち上げて http://127.0.0.1:8000/admin/book/ にアクセス
本を追加できるようになる
自作HTMLでDBを表示させる
$ cd book $ mkdir templates $ cd templates $ mkdir book $ cd book $ touch book_list.html
上記コマンドを入力して下図
{% for item in object_list %} <ul> <li>{{item.title}}</li> <li>{{item.text}}</li> <li>{{item.category}}</li> </ul> {% endfor %}
test/bookproject5/bookproject/book/templates/book/book_list.html に上記を書く
{% for %} はDjangoがhtmlで使える繰り返し文
{{ nyaa }} はDjangoがhtmlで使えるデータ値(の表示)
サーバーを立ち上げて http://127.0.0.1:8000/book/ にアクセス(http://127.0.0.1:8000/admin/book/ ではないので注意)
DetailView(詳細表示の作成)
from django.shortcuts import render from django.views.generic import ListView, DetailView (略) class DetailBookView(DetailView): template_name = 'book/book_detail.html' model = Book
test/bookproject/bookproject/book/views.py に上記を書く
from django.views.generic import ListView, DetailView を追加
class DetailBookView(DetailView): を追加
from django.urls import URLPattern (略) urlpatterns = [ path('book/', views.ListBookView.as_view()), path('book/<int:pk>/detail/', views.DetailBookView.as_view()), ]
test/bookproject/bookproject/book/urls.py に path('book/
pk は Django が自動的にデータに割り振るユニークID (primary_key)
{{ object.category }} {{ object.title }} {{ object.text }}
test/bookproject5/book/templates/book/book_detail.html に上記を書く
サーバーを立ち上げて http://127.0.0.1:8000/book/1/detail/ などにアクセス
Boostrap でデザインを華やかにする
(venv)$ mkdir templates (venv)$ touch templates/base.html
全てのwebページで使いまわすテンプレートの基底ファイルを作る(基底はmanage.pyと同じディレクトリに配置するのが定跡)
<!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.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <title>{% block title%}{% endblock title %}本棚アプリ</title> </head> <body> {% block content %}{% endblock content %} </body> </html>
Boostrap 公式からスターターテンプレートをコピペして test/bookproject/bookproject/base.html に(必要な部分だけ)上記を書く
{% extends 'base.html' %} {% block title %}書籍一覧{% endblock %} {% block content %} {% for item in object_list %} <div class="card"> <h5 class="card-header">{{item.title}}</h5> <div class="card-body"> <p class="card-text">{{item.text}}</p> <a href="#" class="btn btn-primary">Go somewhere</a> <h6 class="card-title">{{item.category}}</h6> </div> </div> {% endfor %} {% endblock content %}
test/bookproject5/bookproject/book/templates/book/book_list.html に上記を書く
base.html で定義した後に各htmlファイルに {% block title %} などを追記、内容を書くことで基底を継承(拡張)する方法でテンプレートデザインを適用できる
{% extends 'base.html' %} {% block title %}書籍一覧{% endblock %} {% block content %} <div class="card"> <h5 class="card-header">{{ object.title }}</h5> <div class="card-body"> <p class="card-text">{{ object.text }}</p> <a href="#" class="btn btn-primary">ボタン</a> <h6 class="card-title">{{ object.category }}</h6> </div> </div> {% endblock content %}
同様に基底を拡張しつつ test/bookproject/bookproject/book/templates/book/book_detail.html に上記を書く
Boostrap 公式から components (card) をコピペしたりすると更に楽が出来る
CreateViewでブラウザ上からデータを作成できるようにする
from django.urls import URLPattern (略) urlpatterns = [ path('book/', views.ListBookView.as_view(), name='list-book'), path('book/<int:pk>/detail/', views.DetailBookView.as_view(), name='detail-book'), path('book/create/', views.CreateBookView.as_view(), name='create-book'), ]
test/bookproject5/bookproject/book/urls.py の urlpatterns に name を追加
なお name は form の遷移先を指定したりするときに使う
from django.shortcuts import render from django.urls import reverse_lazy from django.views.generic import ListView, DetailView, CreateView (略) class CreateBookView(CreateView): template_name = 'book/book_create.html' model = Book fields = ('title', 'text', 'category') success_url = reverse_lazy('list-book')
test/bookproject5/bookproject/book/views.py に上記を書く
from django.urls import reverse_lazy を追加
from django.views.generic import ListView, DetailView, CreateView を追加
class CreateBookView(CreateView): を追加
medel でデータベーステーブルのうちBookを使うと指定
(将来的にデータベーステーブルの種類が増えると予想されるので、ここは正しく指定する必要がある)
fields でデータベースの項目どれを入力フォームにするか指定する(今回は全て指定した)
form の遷移先は success_url で指定する( reverse_lazy は views.py の name から url を逆引きする)
{% extends 'base.html' %} {% block title %}書籍作成{% endblock %} {% block content %} <form method='POST'>{% csrf_token %}{{form.as_p}} <input type='submit' value='作成する'> </form> {% endblock content %}
test/bookproject/bookproject/book/templates/book/book_create.html に上記を書く
form はHTMLタグで method を POST にすると送信ボタンになる
form.as_p はデータベースのフィールド情報に基づいた入力フォームをpタグを自動的に付けて作成してくれる django のテンプレート
なお form を使うときのセキュリティ対策でcsrf_token と必ず書く必要がある(定跡)
サーバーを立てて http://127.0.0.1:8000/book/create/ にアクセス
作成するボタンを押すと下図(http://127.0.0.1:8000/book/)に遷移する
DeleteViewでデータ削除
from django.urls import URLPattern (略) urlpatterns = [ (略) path('book/<int:pk>/delete/', views.DeleteBookView.as_view(), name='delete-book'), ]
test/bookproject5/bookproject/book/urls.py にデータ削除画面の url を追加
{% extends 'base.html' %} {% block title %} 書籍削除 {% endblock %} {% block content %} <form method='POST'>{% csrf_token %} {{form.as_p}} <button type="submit">{{ object.title }}を削除する</button> </form> {% endblock content %}
test/bookproject5/bookproject/book/templates/book/book_delete_confirm.html に上記を書く
from django.shortcuts import render from django.urls import reverse_lazy from django.views.generic import ListView, DetailView, CreateView, DeleteView from .models import Book (略) class DeleteBookView(DeleteView): template_name = 'book/book_confirm_delete.html' model = Book success_url = reverse_lazy('list-book')
test/bookproject/bookproject/book/views.py に上記を書く
from django.views.generic import ListView, DetailView, CreateView, DeleteView を追加
class DeleteBookView(DeleteView): を追加
サーバーを立てて http://127.0.0.1:8000/book/1/delete/ などにアクセス
削除ボタンを押すと(http://127.0.0.1:8000/book/)に遷移する(画像略)
UpdateView ブラウザ上でデータを編集できるようにする
from django.urls import URLPattern (略) urlpatterns = [ (略) path('book/<int:pk>/update/', views.UpdateBookView.as_view(), name='update-book'), ]
test/bookproject5/bookproject/book/urls.py にデータ編集画面の url を追加
from django.shortcuts import render from django.urls import reverse_lazy from django.views.generic import ListView, DetailView, CreateView, DeleteView, UpdateView from .models import Book (略) class UpdateBookView(UpdateView): model = Book fields = (['title', 'text', 'category']) template_name = 'book/book_update.html' success_url = reverse_lazy('list-book')
test/bookproject/bookproject/book/views.py に上記を書く
from django.views.generic import ListView, DetailView, CreateView, DeleteView, UpdateView を追加
class UpdateBookView(UpdateView): を追加
{% extends 'base.html' %} {% block title %} 書籍修正 {% endblock %} {% block content %} <form method='POST'>{% csrf_token %}{{form.as_p}} <button type="submit">修正する</button> </form> {% endblock content %}
test/bookproject5/bookproject/book/templates/book/book_update.html に上記を書く
サーバーを立てて http://127.0.0.1:8000/book/2/update/ などにアクセス
修正するボタンを押すと(http://127.0.0.1:8000/book/)に遷移する(画像略)
リンクの設定
url から直接各画面にアクセスするのでなくwebページのボタンから各画面に遷移できるようにする
{% extends 'base.html' %} {% block title %}書籍一覧{% endblock %} {% block content %} {% for item in object_list %} <div class="card"> <h5 class="card-header">{{item.title}}</h5> <div class="card-body"> <p class="card-text">{{item.text}}</p> <a href="{% url 'detail-book' item.pk %}" class="btn btn-primary">詳細へ</a> <h6 class="card-title">{{item.category}}</h6> </div> </div> {% endfor %} {% endblock content %}
test/bookproject5/bookproject/book/templates/book/book_list.html に上記を書く
<a href="{% url 'detail-book' item.pk %}" class="btn btn-primary">詳細へ</a> を追加
url 'name' は reverse_lazy と同じで名前から url を逆引きしてくれる
なお、データの要素に対するwebページの url を取得するには primary key が必要なので 'name' の後ろに pk を付ける必要がある
{% extends 'base.html' %} {% block title %} 書籍詳細 {% endblock %} {% block content %} <div class="card"> <h5 class="card-header">{{ object.title }}</h5> <div class="card-body"> <p class="card-text">{{ object.text }}</p> <a href="{% url 'list-book' %}" class="btn btn-primary">一覧へ</a> <a href="{% url 'update-book' object.pk %}" class="btn btn-primary">編集する</a> <a href="{% url 'delete-book' object.pk %}" class="btn btn-primary">削除する</a> <h6 class="card-title">{{ object.category }}</h6> </div> </div> {% endblock content %}
test/bookproject5/bookproject/book/book_detail.html に上記を書く
<a href="{% url 'list-book' %}" class="btn btn-primary">一覧へ</a>
<a href="{% url 'update-book' object.pk %}" class="btn btn-primary">編集する</a>
<a href="{% url 'delete-book' object.pk %}" class="btn btn-primary">削除する</a> を追加
サーバーを立てて http://127.0.0.1:8000/book/ にアクセス
レイアウトの調整
<!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.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <title>{% block title%}{% endblock title %}本棚アプリ</title> </head> <body> <nav class="navbar navbar-dark bg-success sticky-top"> <div class="navbar-nav d-flex flex-row"> <a class="nav-link mx-3" href="{% url 'list-book' %}">書籍一覧</a> <a class="nav-link mx-3" href="{% url 'create-book' %}">書籍登録</a> </div> </nav> <div class='p-4'> <h1>{% block h1 %}{% endblock %}</h1> {% block content %}{% endblock content %} </div> </body> </html>
test/bookproject5/bookproject/templates/base.html に上記を書く
{% extends 'base.html' %} {% block title %} 書籍一覧 {% endblock %} {% block h1 %} 書籍一覧 {% endblock %} {% block content %} {% for item in object_list %} <div class="p-4 m-4 bg-light border border-success rounded"> <h2 class="text-success">{{ item.title }}</h2> <h6>カテゴリ:{{ item.category }}</h6> <div class="mt-3"> <a href="{% url 'detail-book' item.pk %}">詳細へ</a> </div> </div> {% endfor %} {% endblock content %}
test/bookproject5/bookproject/book/book_list.html に上記を書く
{% extends 'base.html' %} {% block title %}{{ object.title }}{% endblock %} {% block title %}書籍詳細{% endblock %} {% block content %} <div class="p-4 m-4 bg-light border border-success rounded"> <h2 class="text-success">{{ object.title }}</h2> <p>{{ object.text }}</p> <a href="{% url 'list-book' %}" class="btn btn-primary">一覧へ</a> <a href="{% url 'update-book' object.pk %}" class="btn btn-primary">編集する</a> <a href="{% url 'delete-book' object.pk %}" class="btn btn-primary">削除する</a> <h6 class="card-title">{{ object.category }}</h6> </div> {% endblock content %}
test/bookproject/bookproject/book/book_detail.html に上記を書く