Django/Pythonで「メモ帳」を作成!CRUD操作を学びましょう!

「アフィリエイト広告(Amazonアソシエイト・楽天市場・Yahoo!ショッピング 含む)を利用しています。」

この記事では、Djangoでアプリケーションに必要な機能の基本となる「CROUD操作」について、メモ帳を作成しながら、実装する方法をまとめます。ここで、「CRUD」とは、Create:登録、Read:読み込み、Update:編集、Delete:削除の頭文字を繋げたものです。これらは、アプリケーションをユーザーが操作するために必要となる機能です。この「CRUD」操作が実装できれば、WEBアプリ開発の基本が身につき、ご自身の考えるWEBアプリ開発へと展開できるでしょう。ぜひ参考にしてみてくださいね。

ツール・バージョン情報
・ Windows 10 
・ Windows Subsystem for Linux
・ Ubuntu 20.04 LTS
・ Visual Studio Code
・ Remote WSL 
・ Django 3.2
・ Python 3.8 

上記ツールのインストール方法は、以下の記事でまとめています。「Django/Pythonで「WEBアプリケーション開発に必要な環境構築」の手順まとめ!」をご覧ください。

あわせて読みたい
Django/Pythonで「WEBアプリケーション開発に必要な環境構築」の手順まとめ! 「アフィリエイト広告(Amazonアソシエイト・楽天市場・Yahoo!ショッピング 含む)を利用しています。」 この記事では、DjangoでWEBアプリを作成するために必要となるツ...
目次

WEBアプリケーション制作の手順

以下に、DjangoでWEBアプリケーションを制作するための手順を示します。(UI:User Interface)

STEP
アプリ開発の準備

ディレクトリの作成
仮想環境の構築
Djangoインストール
StartProjecダウンロード
StartAppダウンロード

STEP
UIデザイン

Canvaで全体イメージ作成
HTML&CSS、Bootstrap

STEP
UI機能追加

登録:Create
読込:Read
更新:Update
削除:Delete

STEP
ブラウザ上に表示

WEBアプリケーション制作の結果確認

アプリ開発の準備

アプリ開発の準備では、「Visual Studio Code」でWSL接続し、ターミナル上でコマンド操作を行います。コマンド操作の内容は、「開発ディレクトリの作成」、「仮想環境の構築・有効化」、「Djangoのインストール」、「StartProjectの作成」、「StartAppの作成」を行います。以下に、“操作”と”結果”に分けて、内容を記載します。

(操作) Visual Studio Code - WSL接続 - ターミナル

STEP
ディレクトリ作成

$ mkdir dr
$ ls
→ 「dr」フォルダ作成できていればOK

STEP
仮想環境構築と有効化

$ cd dr
dr $ python3 -m venv venv
dr $ ls
→ 「venv」フォルダ作成できていればOK
dr $ source venv/bin/activate
→ 冒頭が「(venv) ~ $」となっていればOK

STEP
Django、StartProjectの準備

(venv) dr $ pip install django==3.2
(venv) dr $ django-admin startproject drPj
(venv) dr $ ls
→ 「drPj」フォルダが作成できていればOK
(venv) dr $ cd drPj (venv) ../ drPj $ ls
→ 「drPj」フォルダと「manage.py」が存在していることを確認
(venv) dr $ cd drPj (venv) ../ drPj $ ls
→ 「urls.py」、「settings.py」フォルダが存在していることを確認

STEP
Django、StartAppの準備

(venv) ../ accounts $ cd ..
(venv) ../ drPj $ ls
→「manage.py」のフォルダであればOK
(venv) dr / drPj $ python3 manage.py startapp drApp
(venv) dr / drPj $ ls
→ 「drApp」フォルダが作成できていればOK
(venv) dr / drPj $ cd drApp
(venv) ../ drApp $ touch urls.py
(venv) ../ drApp $ ls
→ 「urls.py」、「views.py」、「models.py」フォルダが存在していることを確認

STEP
「manage.py」まで戻る

(venv) ../ drApp $ cd ..
(venv) ../ drPj $ ls
→「manage.py」のフォルダであればOK

(結果) Visual Studio Code - WSL接続 -ファイル - フォルダを開く

(参考) プロジェクト・アプリの概要説明

ここで、今回のプロジェクトとアプリについてのざっくりとした概要を説明します。Djangoは、プロジェクトでアプリを管理していく仕組みです。プロジェクトとして「共通ファイル」を格納する“drPj”ディレクトリ、アプリとして「メモ帳機能」を格納する“drApp”ディレクトリを作成しています。

“drPj”のプロジェクトでは、settings.pyで全体の仕様書を作成し、urls.pyを“drApp”のurls.pyと紐づけることにより、アプリを呼び出せるようにしています。また、htmlの共通箇所やcssで定義するスタイルは“drPj”のディレクトリで管理します。

“drApp”のアプリでは、ブラウザを立ち上げたときのホーム画面や登録・読込・編集・削除の機能を担い、メモ帳の機能を管理します。Djangoには、CreateView、ListView、DetailView、DeleteViewと、「CRUD操作」の機能を実装するための標準機能が備わっているので、これらを用いて実装していきます。

UIデザイン(枠組み)

ここでは、Canvaを用いて全体のイメージをデザインしていき、そのイメージに従い、Visual Studio Codeを用いて、コーディングしていきます。あまり作り込む必要はないですが、コーディングしたHTML&CSSをDjango素材として、アプリに取り込んでいくので、個々のページをどのようにリンクするかについて意識しながら作成しましょう。

デザイン(Canva)

Canvaは、テンプレートが豊富なデザインツールです。無料でも利用できます。作成したデータをpngとして画像出力できます。また、PDF出力とすればリンクが再現できます。感覚的に操作が可能なので、デザインを行う際におすすめです。以下に、Canvaで作成したアプリのデザインを示します。(あくまでデザインです。完成形と全く同じデザイン(イメージ)ではありません。)

  • ⅰ.ホーム
  • ⅱ.登録
  • ⅲ.一覧表示
  • ⅳ.編集-1
  • ⅴ.編集-2
  • ⅵ.編集-3
  • ⅶ.削除-1
  • ⅷ.削除-2
  • ⅸ.削除-3
  • ⅹ.削除-4
スクロールできます
ⅰ.ホーム
ⅱ.登録
ⅲ.一覧表示
ⅳ.編集-1
ⅴ.編集-2
ⅵ.編集-3
ⅶ.削除-1
ⅷ.削除-2
ⅸ削除-3
ⅹ.削除-4

コーディング(HTML&CSS、Bootstrap)

先ほどのデザインをもとにして、コーディングしていきます。作成するhtmlは以下の通りです。コーディングには、HTML&CSSおよび、Bootstrapを使用しています。今回のようにCSS箇所は、Bootstrapを使用しながら仕上げていくと便利です。後述のDjangoで使用するhtmlとタグが異なる箇所もあります(Django仕様に調整していくため)が、概ね外観がイメージできるところまでHTML&CSSおよび、Bootstrapを用いて整えておきます。

  • ⅰ.ホーム
    → HOME.html
  • ⅱ.登録
    → CREATE.html
  • ⅲ.一覧表示
    → READ.html
  • ⅳ.編集-1
    → UPDATE-1.html
  • ⅴ.編集-2
    → UPDATE-2.html
  • ⅵ.削除-3
    → DELETE-3.html
スクロールできます
ⅰ.ホーム
ⅱ.登録
ⅲ.一覧表示
ⅳ.編集-1
ⅴ.編集-2
ⅵ.削除-3

<!doctype html>
<html lang="en">
    <!-- head -->
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>sample</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
        <link rel="stylesheet" href="style.css">
    </head>

    <!-- body -->
    <body>
    <div class="w-25 p-3 text-center">
        <a href="#" class="text-decoration-none link-body-emphasis link-offset-2 link-underline-opacity-25 link-underline-opacity-75-hover">
            <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" fill="currentColor" class="bi bi-house-fill" viewBox="0 0 16 16">
                <path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L8 2.207l6.646 6.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.707 1.5Z"/>
                <path d="m8 3.293 6 6V13.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 13.5V9.293l6-6Z"/>
            </svg>
            <p class="fs-6">ホーム</p>
        </a>
    </div>


    <div  style="width: 50%; transform: translateX(-50%);" class="mt-0 mb-5 position-fixed bottom-0 start-50 translate-middle-x">
        <a href="#" class="btn btn-primary alert alert-success p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
            <span class="fs-6">登録</span>
        </a>
    </div>

    </body>
</html>

<!doctype html>
<html lang="en">
    <!-- head -->
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>sample</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
        <link rel="stylesheet" href="style.css">
    </head>

    <!-- body -->
    <body>
    <div class="w-25 p-3 text-center">
        <a href="#" class="text-decoration-none link-body-emphasis link-offset-2 link-underline-opacity-25 link-underline-opacity-75-hover">
            <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" fill="currentColor" class="bi bi-house-fill" viewBox="0 0 16 16">
                <path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L8 2.207l6.646 6.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.707 1.5Z"/>
                <path d="m8 3.293 6 6V13.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 13.5V9.293l6-6Z"/>
            </svg>
            <p class="fs-6">ホーム</p>
        </a>
    </div>

    <div class="form-floating w-75 mt-0 mb-3 mx-auto align-items-center">
        <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea"></textarea>
        <label for="floatingTextarea">タイトル</label>
    </div>

    <div class="form-floating w-75 mt-0 mb-4 mx-auto align-items-center">
        <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea2" style="height: 200px"></textarea>
        <label for="floatingTextarea2">内容</label>
    </div>

    <div class="container text-center form-floating w-75 mt-0 mb-5 mx-auto align-items-center">
        <div class="row align-items-start">
            <div class="col">
            <a href="#" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span>
                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" class="bi bi-arrow-left-circle" viewBox="0 0 16 16">
                        <path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-4.5-.5a.5.5 0 0 1 0 1H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5H11.5z"/>
                    </svg>
                </span>
            </a>
            </div>
            <div class="col">
            <a href="#" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span class="fs-6">登録</span>
            </a>
            </div>
            <div class="col">
            <a href="#" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span class="fs-6">削除</span>
            </a>
            </div>
        </div>
    </div>

    </body>
</html>

<!doctype html>
<html lang="en">
    <!-- head -->
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>sample</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
        <link rel="stylesheet" href="style.css">
    </head>

    <!-- body -->
    <body>
    <div class="w-25 p-3 text-center">
        <a href="#" class="text-decoration-none link-body-emphasis link-offset-2 link-underline-opacity-25 link-underline-opacity-75-hover">
            <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" fill="currentColor" class="bi bi-house-fill" viewBox="0 0 16 16">
                <path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L8 2.207l6.646 6.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.707 1.5Z"/>
                <path d="m8 3.293 6 6V13.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 13.5V9.293l6-6Z"/>
            </svg>
            <p class="fs-6">ホーム</p>
        </a>
    </div>

    <div class="list-group">
        <a href="#" class="list-group-item list-group-item-action w-75 p-3 mx-auto d-flex justify-content-between align-items-center">
            <span class="fs-6">タイトル1</span>
            <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" class="bi bi-arrow-right-circle" viewBox="0 0 16 16">
                <path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM4.5 7.5a.5.5 0 0 0 0 1h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 1 0-.708.708L10.293 7.5H4.5z"/>
            </svg>
        </a>
        <a href="#" class="list-group-item list-group-item-action w-75 p-3 mx-auto d-flex justify-content-between align-items-center">
            <span class="fs-6">タイトル2</span>
            <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" class="bi bi-arrow-right-circle" viewBox="0 0 16 16">
                <path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM4.5 7.5a.5.5 0 0 0 0 1h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 1 0-.708.708L10.293 7.5H4.5z"/>
            </svg>
        </a>
        <a href="#" class="list-group-item list-group-item-action w-75 p-3 mx-auto d-flex justify-content-between align-items-center">
            <span class="fs-6">タイトル3</span>
            <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" class="bi bi-arrow-right-circle" viewBox="0 0 16 16">
                <path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM4.5 7.5a.5.5 0 0 0 0 1h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 1 0-.708.708L10.293 7.5H4.5z"/>
            </svg>
        </a>
    </div>
    

    <div  style="width: 50%; transform: translateX(-50%);" class="mt-0 mb-5 position-fixed bottom-0 start-50 translate-middle-x">
        <a href="SignUp.html" class="btn btn-primary alert alert-success p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
            <span class="fs-6">登録</span>
        </a>
    </div>

    </body>
</html>

<!doctype html>
<html lang="en">
    <!-- head -->
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>sample</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
        <link rel="stylesheet" href="style.css">
    </head>

    <!-- body -->
    <body>
    <div class="w-25 p-3 text-center">
        <a href="#" class="text-decoration-none link-body-emphasis link-offset-2 link-underline-opacity-25 link-underline-opacity-75-hover">
            <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" fill="currentColor" class="bi bi-house-fill" viewBox="0 0 16 16">
                <path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L8 2.207l6.646 6.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.707 1.5Z"/>
                <path d="m8 3.293 6 6V13.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 13.5V9.293l6-6Z"/>
            </svg>
            <p class="fs-6">ホーム</p>
        </a>
    </div>

    <div class="form-floating w-75 mt-0 mb-3 mx-auto align-items-center">
        <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea" disabled></textarea>
        <label for="floatingTextarea">タイトル3</label>
    </div>

    <div class="form-floating w-75 mt-0 mb-4 mx-auto align-items-center">
        <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea2" style="height: 200px" disabled></textarea>
        <label for="floatingTextarea2">タイトル3の内容</label>
    </div>

    <div class="container text-center form-floating w-75 mt-0 mb-5 mx-auto align-items-center">
        <div class="row align-items-start">
            <div class="col">
            <a href="#" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span>
                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" class="bi bi-arrow-left-circle" viewBox="0 0 16 16">
                        <path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-4.5-.5a.5.5 0 0 1 0 1H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5H11.5z"/>
                    </svg>
                </span>
            </a>
            </div>
            <div class="col">
            <a href="#" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span class="fs-6">編集</span>
            </a>
            </div>
            <div class="col">
            <a href="#" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span class="fs-6">削除</span>
            </a>
            </div>
        </div>
    </div>

    </body>
</html>

<!doctype html>
<html lang="en">
    <!-- head -->
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>sample</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
        <link rel="stylesheet" href="style.css">
    </head>

    <!-- body -->
    <body>
    <div class="w-25 p-3 text-center">
        <a href="#" class="text-decoration-none link-body-emphasis link-offset-2 link-underline-opacity-25 link-underline-opacity-75-hover">
            <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" fill="currentColor" class="bi bi-house-fill" viewBox="0 0 16 16">
                <path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L8 2.207l6.646 6.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.707 1.5Z"/>
                <path d="m8 3.293 6 6V13.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 13.5V9.293l6-6Z"/>
            </svg>
            <p class="fs-6">ホーム</p>
        </a>
    </div>

    <div class="form-floating w-75 mt-0 mb-3 mx-auto align-items-center">
        <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea"></textarea>
        <label for="floatingTextarea">タイトル3(改)</label>
    </div>

    <div class="form-floating w-75 mt-0 mb-4 mx-auto align-items-center">
        <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea2" style="height: 200px"></textarea>
        <label for="floatingTextarea2">タイトル3の内容(修正)</label>
    </div>

    <div class="container text-center form-floating w-75 mt-0 mb-5 mx-auto align-items-center">
        <div class="row align-items-start">
            <div class="col">
            <a href="#" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span>
                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" class="bi bi-arrow-left-circle" viewBox="0 0 16 16">
                        <path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-4.5-.5a.5.5 0 0 1 0 1H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5H11.5z"/>
                    </svg>
                </span>
            </a>
            </div>
            <div class="col">
            <a href="#" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span class="fs-6">保存</span>
            </a>
            </div>
            <div class="col">
            <a href="#" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span class="fs-6">削除</span>
            </a>
            </div>
        </div>
    </div>

    </body>
</html>

<!doctype html>
<html lang="en">
    <!-- head -->
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>sample</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
        <link rel="stylesheet" href="style.css">
    </head>

    <!-- body -->
    <body>
    <div class="w-25 p-3 text-center">
        <a href="#" class="text-decoration-none link-body-emphasis link-offset-2 link-underline-opacity-25 link-underline-opacity-75-hover">
            <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" fill="currentColor" class="bi bi-house-fill" viewBox="0 0 16 16">
                <path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L8 2.207l6.646 6.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.707 1.5Z"/>
                <path d="m8 3.293 6 6V13.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 13.5V9.293l6-6Z"/>
            </svg>
            <p class="fs-6">ホーム</p>
        </a>
    </div>

    <div class="form-floating w-75 mt-0 mb-3 mx-auto align-items-center">
        <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea" disabled></textarea>
        <label for="floatingTextarea">タイトル1</label>
    </div>

    <div class="form-floating w-75 mt-0 mb-4 mx-auto align-items-center">
        <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea2" style="height: 200px" disabled></textarea>
        <label for="floatingTextarea2">タイトル1の内容</label>
    </div>

    <div class="container text-center form-floating w-75 mt-0 mb-5 mx-auto align-items-center">
        <div class="row align-items-start">
        <div class="col">
            <a href="#" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span>
                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" class="bi bi-arrow-left-circle" viewBox="0 0 16 16">
                        <path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-4.5-.5a.5.5 0 0 1 0 1H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5H11.5z"/>
                    </svg>
                </span>
            </a>
        </div>
        <div class="col">
            <a href="#" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span class="fs-6">編集</span>
            </a>
        </div>
        <div class="col">
            <a href="javascript:void(0);" onclick="confirmDelete()" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span class="fs-6">削除</span>
            </a>
        </div>
        </div>
    </div>

    <!-- script -->
    <script>
    function confirmDelete() {
        if (confirm("本当に削除しても良いですか?")) {
            // 「OK」が選択された時の処理をここに記述
            console.log("削除処理が承認されました。");
        } else {
            // 「キャンセル」が選択された時の処理をここに記述
            console.log("削除処理がキャンセルされました。");
        }
    }
    </script>
    </body>
</html>

UI機能追加(CRUD操作)

今回は、ブラウザ上で「メモ帳のタイトル・内容」をデータベースとして扱うための機能を追加する必要があります。WEBアプリ制作に取り掛かる前に、追加すべき機能について、整理しておきましょう。今回は、ホーム →登録 → 読込(リスト表示) → 編集 → 読込(リスト表示) → 削除 → 読込(リスト表示)の流れで、UIデザインが構成されています。追加すべき機能は、以下に示すCRUD操作の順でまとめるとわかりやすいです。具体的な手順は、「プロジェクトの制作」と「アプリの制作」で説明します。

登録(Create)

ホームから、登録画面へ進み、タイトルと内容を記載して登録ボタンを押すことで、メモ情報をデータベースに保存するところまでの機能を追加していきます。ここでは、「CreateView」というDjangoの標準機能を用いて作成します。

読込(Read)

次に、ホーム画面にメモのタイトル一覧を表示する機能を追加します。こちらは、「ListView」というDjangoの標準機能を用いて作成していきます。また、編集画面でタイトルと内容の詳細が表示される機能を追加します。こちらは、「DetailView」というDjangoの標準機能を用いて作成していきます。

編集(Update)

登録で作成したメモのタイトルや内容を編集する機能を追加します。まず、編集画面へ移動した際は、画面ロックをかけて編集不可の状態とします。次に編集ボタンを押すと画面ロックを解除してユーザーが編集できるようにコントロールします。こちらは、「UpdateView」というDjangoの標準機能を用いて作成していきます。

削除(Delete)

登録したメモを削除する機能を追加します。編集画面から削除ボタンを押すことで、削除を実行できるようにします。その際には、誤ってユーザーが削除ボタンを押してもそのまま削除されないように、「本当に削除しても良いですか?」というポップアップ画面もワンクッション設置しておきます。こちらは、「DeleteView」というDjangoの標準機能を用いて作成していきます。

ブラウザ上に表示

「Visual Studio Code」でWSL接続し、ターミナル上で、「python3 manage.py runserver」のコマンドを実行して、ブラウザ上(http://127.0.0.1:8000)で表示します。“操作”と”結果”の内容は、後述します。

プロジェクトの制作

StartPrijectのファイル構成は、前述(「アプリ制作手順」のアプリ開発の準備)の通りです。すべて必要なファイルではあるものの、WEBアプリを制作する上では「setting.py」と「url.py」の内容をカスタマイズしていきます。具体的には、setting.pyではどんな指示を出すのか、url.pyでは、どのURLにアクセスさせるのかを設定していきます。

  • setting.py
    → プロジェクトにアプリを認識
      templatesディレクトリを認識
      staticディレクトリを認識
  • urls.py
    → アプリを表示するURLを設定
  • templates
    → html格納場所
      html共通箇所のbase.html作成
  • static
    →  cssの認識と格納

setting.py

ここでは、プロジェクトにアプリを認識させるために、「setting.py」にコードを追記します。また、この後templatesやstaticというhtmlとcssを格納するディレクトリを新たに作成するので、このディレクトリを認識させるためのコードも追記します。具体的には、以下の通りです。

  • 「drApp」フォルダにあるapp,pyのclassで定義されている「DrappConfig」を認識させる。
  • DIRSの内容を「BASE_DIR / ‘templates’」として、htmlのテンプレートディレクトリを認識させる。
  • STATICFILES_DIRSでstaticのディレクトリを認識させる。
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'drApp.apps.DrappConfig',
]

TEMPLATES = [
・・・
        'DIRS': [BASE_DIR / 'templates'],
・・・
]

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    BASE_DIR / 'static',
]

urls.py

次に、アプリを表示するURLを定義します。ここは、ブラウザにアクセスした際にアプリを表示させるためにpathは’’で定義しています。

from django.contrib import admin
from django.urls import path , include

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

templates

プロジェクトの中に格納するファイルは、htmlの共通部分となる箇所です。前述のhtmlでは、<head>タグ内や<body>の中でもホームに係る箇所は、共通となります。これをまとめておいて、あとから呼び出せるように設定します。具体的には、「manage.py」があるディレクトリに“templates”というディレクトリを作成し、その中にbase.htmlというファイルを作成します。このbase.htmlの中にhtmlで共通する箇所をまとめて記述しておきます。

(操作) Visual Studio Code - WSL接続 - ターミナル

STEP
「manage.py」がある場所にディレクトリ作成

(venv) ../ drPj $ mkdir templates

STEP
bsase.html作成

(venv) ../ drPj $ touch templates/base.html

(コード)base.html

  • {% load static %}
    → cssを呼び出します。(後述)
  • {% static ‘css/style.css’ %}
    → cssの読み込み先を指定します。(後述)
  • {% url ‘home’ %}
    → name=homeのurlリンクを指定します。(後述)
  • {% block content %}~{% endblock content %}
    → base.htmlを呼び出したhtmlの内容が追加される場所です。(後述)

<!doctype html>
<html lang="en">
    <!-- head -->
    <head>
        {% load static %}
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>sample</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
        <link rel="stylesheet" href="{% static 'css/style.css' %}">
    </head>

    <!-- body -->
    <body>
    <div id="container">
        <div class="d-flex justify-content-between">
        <div class="w-25 p-3 text-center">
            <a href="{% url 'home' %}" class="text-decoration-none link-body-emphasis link-offset-2 link-underline-opacity-25 link-underline-opacity-75-hover">
                <svg xmlns="http://www.w3.org/2000/svg" width="35" height="35" fill="currentColor" class="bi bi-house-fill" viewBox="0 0 16 16">
                    <path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L8 2.207l6.646 6.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.707 1.5Z"/>
                    <path d="m8 3.293 6 6V13.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 13.5V9.293l6-6Z"/>
                </svg>
                <p class="fs-6">ホーム</p>
            </a>
        </div>
        </div>
        {% block content %}{% endblock content %}
    </div>
    </body>
</html>



static/css

cssのファイルは、staticというディレクトリを作成して格納します。前述の通り、seettingにてプロジェクトへの認識は行っております。

(操作) Visual Studio Code - WSL接続 - ターミナル

STEP
「manage.py」がある場所からディレクトリ作成

(venv) ../ drPj $ mkdir static
(venv) ../ drPj $ mkdir static/css

STEP
style.css作成

(venv) ../ drPj $ touch static/css/style.css

(コード)style.css

  • @media
    → レスポンシブ対応のため、サイズ調整します。

@media (min-width: 768px) {
    #container {
        width: 50%;
        margin: auto;
    }
}

@media (max-width: 767px) {
    #container {
        width: 100%;
        margin: auto;
    }
}



アプリの制作

StartAppのファイル構成は、前述(「アプリ制作手順」のアプリ開発の準備)の通りです。すべて必要なファイルではあるものの、WEBアプリを制作する上では「url.py」、「views.py」、「models.py」の内容をカスタマイズしていきます。具体的には、url.pyではプロジェクトに続くURLを設定し、views.pyではどんな表示内容とするのか、models.pyでは表示内容の素材を設定していきます。

  • urls.py
    → アプリを表示するURLを設定(プロジェクトURLに続くもの)
  • views.py
    → 呼び出すモデルの関数やクラスを定義
  • models.py
    → データベースのモデル(配列)を定義
  • templates
    → base.htmlを呼び出して、各htmlに係るコードを記入する。

urls.py

ここからは、ブラウザにアクセスしたときに表示される「ホーム画面」の設定をしていきます。先ほどプロジェクト側で、アプリの「urls.py」をincludeして認識させています。それに続くURLをアプリの「urls.py」で定義します。ブラウザにアクセスした際にアプリを表示させるために、ここでもpathは’’で定義しています。続いて、「views.py」で読み込む関数を定義しています。また、「DetailDataView」、「UpdateDataView」、「DeleteDataView」のpathに続く<int:pk>は、具体的にどのメモのDetail=詳細、Update=編集、Delete=削除かを認識するために、メモのid番号を指定するように設定しています。

from django.urls import path
from . import views

urlpatterns = [
    path('create/', views.CreateDataView.as_view(), name='create'),
    path('', views.HomeView.as_view(), name='home'),
    path('<int:pk>/detail/', views.DetailDataView.as_view(), name='detail'),
    path('<int:pk>/update/', views.UpdateDataView.as_view(), name='update'),
    path('<int:pk>/delete/', views.DeleteDataView.as_view(), name='delete'),
]

views.py

上記の「urls.py」にて、読み込まれるクラス「~View」を定義します。ここで、定義したレスポンスの内容が「urls.py」で定義したURLを介してブラウザに表示されるようになります。

  • template_name
    → 各Viewに対するhtmlを指定
  • model
    → 使用するモデル名を指定(models.pyで定義(後述))
  • fields
    → データベースの項目(models.pyで定義(後述))
  • success_url
    → 操作完了後に表示するhtmlを指定
from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic import (
    CreateView,
    ListView,
    DetailView,
    UpdateView,
    DeleteView,
    )
from .models import Memo

# Create your views here.
class CreateDataView(CreateView):
    template_name = 'create.html'
    model = Memo
    fields = ['title', 'description']
    success_url = reverse_lazy('home')

class HomeView(ListView):
    template_name = 'home.html'
    model = Memo

class DetailDataView(DetailView):
    template_name = 'detail.html'
    model = Memo

class UpdateDataView(UpdateView):
    template_name = 'update.html'
    model = Memo
    fields = ['title', 'description']
    success_url = reverse_lazy('home')

class DeleteDataView(DeleteView):
    template_name = 'delete.html'
    model = Memo
    success_url = reverse_lazy('home')

models.py

今回作成のメモ帳では、「タイトル:title」と「内容:description」の配列を定義して、データベースとして扱います。それらの定義をmodels.pyで行います。前述のMemoというクラス名で定義することで、viewsとリンクします。

  • def __str__(self):
    → 管理画面で表示ずるリストをタイトル名で返します(後述)
from django.db import models

# Create your models here.
class Memo(models.Model):
    title = models.CharField(max_length=100)
    description = models.TextField()

    def __str__(self):
        return self.title

また、管理画面でMemoのモデルを表示させるには、drApp/admin.pyに以下を記述します。

from django.contrib import admin
from .models import Memo

# Register your models here.
admin.site.register(Memo)

templates

ここでは、「ホーム画面」に該当するhtmlを格納します。また、プロジェクトのtemplatesでは、base.htmlという共通箇所まとめを作成したので、これを呼び出す形式で“各種 html”を作成していきます。具体的な手順は以下です。

(操作) Visual Studio Code - WSL接続 - ターミナル

STEP
「manage.py」がある場所からディレクトリ作成

(venv) ../ drPj $ mkdir drApp/templates

STEP
.html作成

(venv) ../ drPj $ touch drApp/temlpates/create.html
(venv) ../ drPj $ touch drApp/temlpates/home.html
(venv) ../ drPj $ touch drApp/temlpates/detail.html
(venv) ../ drPj $ touch drApp/temlpates/update.html
(venv) ../ drPj $ touch drApp/temlpates/delete.html

(コード)各種 html

  • {% extends ‘base.html’ %}
    → base.htmlを呼び出します。
  • {% block content %}~{% endblock content %}
    → html共通箇所以外のコードを記入します。
  • {% url ‘各種 name’ %}
    → 各種 htmlのnameを指定して該当urlへリンクします。
  • {% csrf_token %}
    → formタグには必須のコードを記入します。
  • {% if form.title.errors %}~{{ form.title.errors.as_text }}~{% endif %}
    → 入力エラーがあれば、エラーの文章を返します。
  • {% for item in object_list %}~{ item.title }~{% endfor %}
    → データベースを読み込んでタイトルをリストで表示します。
  • {{ object.title }}、{{ object.description }}
    → urlのidに対するタイトルと内容を読み込みます。

{% extends 'base.html' %}

{% block content %}

<form method="POST" class="w-75 mx-auto">
    {% csrf_token %}
    
    <div class="form-floating mb-3">
        <textarea class="form-control" placeholder="タイトル" id="floatingTextareaTitle" name="title"></textarea>
        <label for="floatingTextareaTitle">タイトル</label>
        {% if form.title.errors %}
            <div class="mt-2 alert alert-danger" role="alert">
                {{ form.title.errors.as_text }}
            </div>
        {% endif %}
    </div>
    
    <div class="form-floating mb-4">
        <textarea class="form-control" placeholder="内容" id="floatingTextareaDescription" name="description" style="height: 200px"></textarea>
        <label for="floatingTextareaDescription">内容</label>
        {% if form.title.errors %}
            <div class="mt-2 alert alert-danger" role="alert">
                {{ form.title.errors.as_text }}
            </div>
        {% endif %}
    </div>

    <div class="container text-center form-floating w-100 mt-0 mb-5 mx-auto align-items-center">
        <div class="row align-items-start">
            <div class="col">
            <a href="{% url 'home' %}" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span>
                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" class="bi bi-arrow-left-circle" viewBox="0 0 16 16">
                        <path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-4.5-.5a.5.5 0 0 1 0 1H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5H11.5z"/>
                    </svg>
                </span>
            </a>
            </div>
            <div class="col">
            <div class="text-center mb-5">
                <span class="fs-6">
                    <input type="submit" value="登録" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center">
                </span>
            </div>
            </div>
            <div class="col">
            <a href="#" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span class="fs-6">-</span>
            </a>
            </div>
        </div>
    </div>

</form>

{% endblock content %}

{% extends 'base.html' %}

{% block content %}
    
    <div class="list-group container text-center form-floating w-75 mt-0 mb-5 mx-auto align-items-center">
        {% for item in object_list %}
            <a href="{% url 'detail' pk=item.pk %}" class="list-group-item list-group-item-action w-100 p-3 mx-auto d-flex justify-content-between align-items-center">
                <span class="fs-6">{{ item.title }}</span>
                <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" class="bi bi-arrow-right-circle" viewBox="0 0 16 16">
                    <path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM4.5 7.5a.5.5 0 0 0 0 1h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 1 0-.708.708L10.293 7.5H4.5z"/>
                </svg>
            </a>
        {% endfor %}
    </div>

    <div style="width: 50%; transform: translateX(-50%);" class="mt-0 mb-5 position-fixed bottom-0 start-50 translate-middle-x">
        <a href="{% url 'create' %}" class="btn btn-primary alert alert-success p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
            <span class="fs-6">登録</span>
        </a>
    </div>

{% endblock content %}

{% extends 'base.html' %}

{% block content %}

    <div class="container text-center form-floating w-75 mt-0 mb-5 mx-auto align-items-center">    
        <div class="form-floating mb-3">
            <textarea class="form-control" placeholder="タイトル" id="floatingTextareaTitle" name="title" disabled>
                {{ object.title }}
            </textarea>
            <label for="floatingTextareaTitle">タイトル</label>
        </div>
        <div class="form-floating mb-4">
            <textarea class="form-control" placeholder="内容" id="floatingTextareaDescription" name="description" style="height: 200px" disabled>
                {{ object.description }}        
            </textarea>
            <label for="floatingTextareaDescription">内容</label>
        </div>
    </div>

    <div class="container text-center form-floating w-75 mt-0 mb-5 mx-auto align-items-center">
        <div class="row align-items-start">
            <div class="col">
            <a href="{% url 'home' %}" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span>
                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" class="bi bi-arrow-left-circle" viewBox="0 0 16 16">
                        <path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-4.5-.5a.5.5 0 0 1 0 1H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5H11.5z"/>
                    </svg>
                </span>
            </a>
            </div>
            <div class="col">
            <div class="text-center mb-5">
                <a href="{% url 'update' pk=object.pk %}" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                    <span class="fs-6">編集</span>
                </a>
            </div>
            </div>
            <div class="col">
            <a href="{% url 'delete' pk=object.pk %}" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span class="fs-6">削除</span>
            </a>
            </div>
        </div>
    </div>

{% endblock content %}

{% extends 'base.html' %}

{% block content %}

<form method="POST" class="w-75 mx-auto">
    {% csrf_token %}

    <div class="container text-center form-floating w-100 mt-0 mb-5 mx-auto align-items-center">    
        <div class="form-floating mb-3">
            <textarea class="form-control" placeholder="タイトル" id="floatingTextareaTitle" name="title">
                {{ object.title }}
            </textarea>
            <label for="floatingTextareaTitle">タイトル</label>
        </div>
        <div class="form-floating mb-4">
            <textarea class="form-control" placeholder="内容" id="floatingTextareaDescription" name="description" style="height: 200px">
                {{ object.description }}        
            </textarea>
            <label for="floatingTextareaDescription">内容</label>
        </div>
    </div>

    <div class="container text-center form-floating w-100 mt-0 mb-5 mx-auto align-items-center">
        <div class="row align-items-start">
            <div class="col">
            <a href="{{ request.META.HTTP_REFERER }}" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span>
                    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" class="bi bi-arrow-left-circle" viewBox="0 0 16 16">
                        <path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-4.5-.5a.5.5 0 0 1 0 1H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5H11.5z"/>
                    </svg>
                </span>
            </a>
            </div>
            <div class="col">
            <div class="text-center mb-5">
                <span class="fs-6">
                    <input type="submit" value="保存" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center">
                </span>
            </div>
            </div>
            <div class="col">
            <a href="{% url 'delete' pk=object.pk %}" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center" role="alert">
                <span class="fs-6">削除</span>
            </a>
            </div>
        </div>
    </div>
    
</form>

{% endblock content %}

{% extends 'base.html' %}

{% block content %}

    <div class="w-75 mx-auto text-center mt-5">
        <h2>”{{ object.title }}”を削除しますか?</h2>
        <form method="POST" action="{% url 'delete' pk=object.pk %}">
            {% csrf_token %}
            <div class="mt-5 row align-items-start">
                <div class="col">
                    <button type="submit" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center">はい</button>
                </div>
                <div class="col">
                    <a href="{{ request.META.HTTP_REFERER }}" class="btn btn-primary alert alert-success w-100 p-3 mx-auto d-flex justify-content-evenly align-items-center">いいえ</a>
                </div>
            </div>
        </form>
    </div>

{% endblock content %}


ブラウザ上に公開

ここからご紹介するのは、ローカル上でブラウザに公開する方法です。具体的には、「Visual Studio Code」でWSL接続し、ターミナル上で、「python3 manage.py runserver」のコマンドを実行して、ブラウザ上(http://127.0.0.1:8000)で表示します。また、“superuser”を定義することで、管理画面にログインして、編集や削除を管理者のみが行える設定も行います。以下に、“操作”と”結果”に分けて、内容を記載します。なお、ネットワーク上にデプロイ・公開するには、Herokuというツールを用いると簡単な操作で公開までが可能となるのでおすすめです。(今回は、ネットワーク上にデプロイ・公開する方法は、割愛します。)

一般ユーザーの画面

(操作) Visual Studio Code - WSL接続 - ターミナル

STEP
manage.pyがあるディレクトリからスタート

(venv) ../ drPj $

STEP
データベースの更新

(venv) ../ drPj $ python3 manage.py migrate

STEP
ローカルサーバー接続

(venv) ../ drPj $ python3 manage.py runserver

STEP
ブラウザにアクセス
http://127.0.0.1:8000

(結果) ブラウザ上で表示

以下のようにブラウザに表示できれば完了です。それぞれのボタンも正しくリンクできているか確認しましょう。

スクロールできます
ⅰ.ホーム
ⅱ.登録-1
ⅲ.登録-2
ⅳ.一覧表示-1
ⅴ.一覧表示-2
ⅵ.編集-1
ⅶ.編集-2
ⅷ.編集-3
ⅸ.削除-1
ⅹ.削除-2
ⅺ.削除-3

管理者の画面

(操作) Visual Studio Code - WSL接続 - ターミナル

STEP
「manage.py」がある場所からsuperuser作成

(venv) ../ drPj $
(venv) ../ drPj $python3 manage.py createsuperuser
→ ユーザー名、メールアドレス、パスワード、パスワード確認用を入力

STEP
ローカルサーバー接続

(venv) ../ drPj $ python3 manage.py runserver

STEP
ブラウザにアクセス

http://127.0.0.1:8000/admin/ でアクセスしてsuperuserとしてログイン
→ 登録した情報の閲覧・編集・削除できる。

(結果) ブラウザ上で表示

管理画面で登録したユーザー名とパスワードを入力して、以下のようにブラウザに表示できれば完了です。「users」をクリックすれば、新規登録や編集・削除ができます。

おさらいとコード集

さて、「Django/Pythonで「メモ帳」を作成!CRUD操作を学びましょう!」のおさらいです。今回は、DjangoでWEBアプリケーション開発する際の制作手順、プロジェクトの作成、アプリの制作、ブラウザ上に表示の流れで、WEBアプリケーション開発のための知識まとめをテーマとしてご紹介させて頂きました。以下に、この記事の内容を取りまとめます。

WEBアプリケーション制作の手順

アプリ開発の準備:Visual Studio Codeを使用して、ディレクトリの作成、仮想環境の構築、Djangoのインストール、StartProjectとStartAppのダウンロードを行う方法を記載しています。UIデザイン:Canvaを使用して全体のイメージを作成し、HTML&CSSおよびBootstrapでコーディングした結果を示しています。UI機能追加(CRUD操作):CRUD操作に基づいてユーザー情報をデータベースとして扱うための機能追加に関する概要を示しています。詳細は、「プロジェクトの制作」と「アプリの制作」で後述としています。ブラウザ上に表示:ここでは、手順の概要のみ述べて、詳細は「ブラウザに表示」で後述としています。

スクロールできます
ⅰ.ホーム
ⅱ.登録
ⅲ.一覧表示
ⅳ.編集-1
ⅴ.編集-2
ⅵ.編集-3
ⅶ.削除-1
ⅷ.削除-2
ⅸ削除-3
ⅹ.削除-4

スクロールできます
ⅰ.ホーム
ⅱ.登録
ⅲ.一覧表示
ⅳ.編集-1
ⅴ.編集-2
ⅵ.削除-3

プロジェクトの制作

StartProjectのファイル構成:「settings.py」と「urls.py」の内容をカスタマイズします。settings.pyではアプリをプロジェクトに認識させ、urls.pyでアプリの表示するURLを定義するための具体的なコード内容を示しています。また、templatesとstatic/cssの作成により、共通のHTML構造とCSSスタイリングを格納する手順を示しています。なお、「templates/base.html」と「static/css/style.css」のコードは、【2│プロジェクトの制作】に掲載済みなのでそちらをご覧ください。

※ settings.pyにおける「SECRET_KEY」の箇所は、プロジェクトをご自身の開発環境でダウンロードした際のものに置き換える必要があります。コードはあくまで参照としてご利用ください。

"""
Django settings for drPj project.

Generated by 'django-admin startproject' using Django 3.2.

For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""

from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'SECRET_KEY'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'drApp.apps.DrappConfig',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'drPj.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'drPj.wsgi.application'


# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    BASE_DIR / 'static',
]

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

"""drPj URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path , include

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


アプリの制作

StartAppのファイル構成:urls.py、views.py、models.pyの内容をカスタマイズします。また、管理画面で新規作成・編集や削除の操作できるようにadmin.pyの内容もカスタマイズします。url.pyでプロジェクトに続くURLを設定し、views.pyで表示内容を定義するための具体的なコード内容を示しています。また、タイトルと内容をメモ帳のデータベースとして扱うためにmodels.pyでそれらを定義しています。また、templatesとアプリに必要となるHTMLの作成手順についても説明しています。なお、templatesに格納する「drApp/templates/各種html」のコードは、【3│アプリの制作】に掲載済みなのでそちらをご覧ください。

from django.urls import path
from . import views

urlpatterns = [
    path('create/', views.CreateDataView.as_view(), name='create'),
    path('', views.HomeView.as_view(), name='home'),
    path('/detail/', views.DetailDataView.as_view(), name='detail'),
    path('/update/', views.UpdateDataView.as_view(), name='update'),
    path('/delete/', views.DeleteDataView.as_view(), name='delete'),
]

from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic import (
    CreateView,
    ListView,
    DetailView,
    UpdateView,
    DeleteView,
    )
from .models import Memo

# Create your views here.
class CreateDataView(CreateView):
    template_name = 'create.html'
    model = Memo
    fields = ['title', 'description']
    success_url = reverse_lazy('home')

class HomeView(ListView):
    template_name = 'home.html'
    model = Memo

class DetailDataView(DetailView):
    template_name = 'detail.html'
    model = Memo

class UpdateDataView(UpdateView):
    template_name = 'update.html'
    model = Memo
    fields = ['title', 'description']
    success_url = reverse_lazy('home')

class DeleteDataView(DeleteView):
    template_name = 'delete.html'
    model = Memo
    success_url = reverse_lazy('home')

from django.db import models

# Create your models here.
class Memo(models.Model):
    title = models.CharField(max_length=100)
    description = models.TextField()

    def __str__(self):
        return self.title

from django.contrib import admin
from .models import Memo

# Register your models here.
admin.site.register(Memo)



ブラウザ上に表示

ローカルでの公開方法:Visual Studio CodeでWSL接続し、ターミナルでpython3 manage.py runserverコマンドを実行し、ブラウザで表示する方法を紹介しています。ネットワーク上にデプロイ・公開する方法は、Heroku(有料)を使用することを推奨していますが、その詳細は省略しています。この記事では、下記画面がブラウザ上に表示されていること・ボタンのリンクが正しいことを確認して完了です。

スクロールできます
ⅰ.ホーム
ⅱ.登録-1
ⅲ.登録-2
ⅳ.一覧表示-1
ⅴ.一覧表示-2
ⅵ.編集-1
ⅶ.編集-2
ⅷ.編集-3
ⅸ.削除-1
ⅹ.削除-2
ⅺ.削除-3

また、superuserの作成による管理画面へのアクセスについても説明しています。管理画面にログインすれば、「一般ユーザー」の新規作成登録および、編集・削除ができるようになります。

おわりに

この記事では、Djangoで「メモ帳を作成しながらCRUD操作」の実装方法について、基本的なツールのインストールから具体的なコーディング手法、そしてローカルサーバーでのアプリ公開までの一連の流れを解説しました。このプロセスを通じて、Djangoを用いたWEBアプリケーション開発の基礎を理解し、さらに応用的な開発へと進むための土台を築くことができます。この基礎を土台として、さらに複雑な機能やデザインを加えたアプリケーションの開発にチャレンジしてみてください。プログラミングは試行錯誤の連続ですが、この記事で取り上げた内容やコードなどが、皆さんのプログラミング学習のお役に立てれば幸いです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次