2024-12-22

ChatGPTを使って、アプリ開発(ワークフロー6):開発4

 前回のChatGPTの提案で作成したコードで開発環境サーバーを起動したところ、「django.core.exceptions.FieldError: Unknown field(s) (assigned_user) specified for Task」というエラーが出ました。これは、最初に作成した「Task」モデルに「assigned_user」というフィールドがないためです。次の担当者を示す「assigned_user」ですが、ワークフローの実行時に選択するようにするので、form.pyを以下のように変更します。

タイトル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from django import forms
from .models import Flow, Task
 
 
class FlowForm(forms.ModelForm):
    class Meta:
        model = Flow
        fields = ['name']
 
 
class TaskForm(forms.ModelForm):
    class Meta:
        model = Task
        fields = ['name']さ

最初の画面の「新規作成」をクリックします。






「django.template.exceptions.TemplateDoesNotExist: flow_create.html」というエラーが発生しました。これは、「flow_create.html」というテンプレートとが存在しないためです。ChatGPTの指示で作成したテンプレートは「flow_create_edit.html」なので、views.pyを以下のように修正します。

タイトル
1
2
3
4
5
6
7
# フロー作成ビュー
def flow_create(request):
    if request.method == 'POST':
        # フォームの処理を追加
        # 新しいフローを作成し保存する
        return redirect('flow_list')
    return render(request, 'workflow/flow_create_edit.html')

前の画面に戻って、再度、「新規作成」をクリックします。

以下のような画面が開きます。








フロー作成画面について、もう少し画面構成や機能を追加していきます。まずは、ChatGPTに要求を伝えます。


ChatGPTへのスクリプト:

タスクを作成するエリアを四角で囲み、その中に「START」と「END」というボックス(角丸のボックス)をデフォルトで作成し、「タスク作成」ボタンをクリックしたら、タスク名を入力する欄と、「入力項目設定」というボタンを中に含んだボックス(角丸のボックス)を追加したい。なお、タスクを作成するエリアは可変とし、画面オーバーした場合はスクロールできるようにする。デフォルトの大きさは、1200px×600pxにしたい。 「START」のボックスは左上に、「END」のボックスは右下に配置したい。


ChatGPTからの回答:

この要件に基づいて、Djangoのテンプレートに必要なHTMLとCSSを追加し、タスク作成エリアを実装します。タスクエリアは可変で、デフォルトのサイズが1200px × 600pxとなり、スクロールが可能な仕組みにします。また、「START」と「END」のボックスは指定の位置に配置し、タスクを追加できるようにします。

1. HTMLとCSSのテンプレート

タイトル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>フロー作成</title>
    <link rel="stylesheet" href="{% static 'css/styles.css' %}">
    <style>
        /* タスク作成エリアのスタイル */
        #task-area {
            width: 1200px;
            height: 600px;
            border: 2px solid #ccc;
            position: relative;
            overflow: auto;
            padding: 10px;
        }
 
        /* STARTとENDボックスのスタイル */
        .task-box {
            width: 150px;
            height: 50px;
            border-radius: 10px;
            background-color: #f0f0f0;
            border: 2px solid #333;
            display: flex;
            justify-content: center;
            align-items: center;
            position: absolute;
            text-align: center;
        }
 
        #start-box {
            top: 10px;
            left: 10px;
        }
 
        #end-box {
            bottom: 10px;
            right: 10px;
        }
 
        /* 新しいタスクボックスのスタイル */
        .new-task-box {
            width: 200px;
            height: 100px;
            border-radius: 10px;
            background-color: #e0f7fa;
            border: 2px solid #00796b;
            position: absolute;
            padding: 10px;
            box-sizing: border-box;
        }
 
        .new-task-box input[type="text"] {
            width: 90%;
            margin-bottom: 10px;
        }
 
        .new-task-box button {
            width: 100%;
        }
 
        /* タスクエリア全体がスクロール可能な場合のスタイル */
        #task-area::-webkit-scrollbar {
            width: 12px;
            height: 12px;
        }
 
        #task-area::-webkit-scrollbar-thumb {
            background-color: #888;
            border-radius: 10px;
        }
 
        #task-area::-webkit-scrollbar-track {
            background-color: #f0f0f0;
        }
 
    </style>
</head>
<body>
    <h1>フロー作成</h1>
 
    <!-- タスク作成エリア -->
    <div id="task-area">
        <!-- STARTボックス -->
        <div id="start-box" class="task-box">
            START
        </div>
 
        <!-- ENDボックス -->
        <div id="end-box" class="task-box">
            END
        </div>
    </div>
 
    <!-- タスク作成ボタン -->
    <button type="button" id="add-task-btn">タスク作成</button>
 
    <script>
        let taskCounter = 0;  // タスクIDカウンター
 
        // タスク作成ボタンのクリックイベント
        document.getElementById('add-task-btn').addEventListener('click', function() {
            taskCounter += 1;
            const taskArea = document.getElementById('task-area');
 
            // 新しいタスクボックスを作成
            const newTaskBox = document.createElement('div');
            newTaskBox.className = 'new-task-box';
            newTaskBox.style.top = (50 + taskCounter * 60) + 'px';  // 位置を動的に設定
            newTaskBox.style.left = (50 + taskCounter * 60) + 'px'; // 位置を動的に設定
 
            // タスク名入力欄を追加
            const taskInput = document.createElement('input');
            taskInput.type = 'text';
            taskInput.placeholder = 'タスク名を入力';
 
            // 入力項目設定ボタンを追加
            const settingsButton = document.createElement('button');
            settingsButton.textContent = '入力項目設定';
 
            // 新しいタスクボックスに要素を追加
            newTaskBox.appendChild(taskInput);
            newTaskBox.appendChild(settingsButton);
 
            // タスクエリアに新しいタスクボックスを追加
            taskArea.appendChild(newTaskBox);
        });
    </script>
</body>
</html>


2. 説明

  1. タスク作成エリア (#task-area):
    このエリアはスクロール可能で、デフォルトのサイズが1200px × 600pxです。overflow: autoで、内容がエリアを超える場合にスクロールできるようにしています。

  2. STARTENDのボックス:
    #start-box#end-boxというIDを持った角丸のボックスがそれぞれ左上(top: 10px; left: 10px;)と右下(bottom: 10px; right: 10px;)に配置されています。

  3. タスク作成ボタン:
    「タスク作成」ボタンをクリックすると、タスク名を入力する欄と「入力項目設定」ボタンを含んだ新しいタスクボックスがタスクエリアに動的に追加されます。ボックスは自動的に位置が調整されますが、最初はtopleftの位置を固定的にずらすことで配置しています。

  4. JavaScript:
    タスク作成ボタンをクリックすることで、new-task-boxが作成され、タスクエリア内に追加されます。taskCounterによって、ボックスが配置される位置が異なるように動的に調整されます。

3. 今後の拡張

  • タスクのドラッグ&ドロップ:
    例えば、jsPlumbDragulaなどのライブラリを利用して、タスクボックスをドラッグ&ドロップで移動可能にすることができます。

  • 入力項目設定のポップアップ:
    「入力項目設定」ボタンをクリックした際に、ポップアップウィンドウを表示して、入力項目を動的に追加・削除する機能を実装することができます。

  • タスク間の接続:
    タスク間を線で結ぶ機能も後で追加することで、ワークフローの全体像を視覚化できます。

このテンプレートを基に、実際のワークフロー作成・編集機能のユーザーインターフェースを実装することができます。


このページを再表示すると、以下のような画面になります。









左下の「タスク作成」ボタンをクリックすると、タスクのボックスが表示されます。









ChatGPTへのスクリプト:

flow_create_edit.htmlでタスクを作成後、そのタスクボックスにある「入力項目設定ボタン」をクリックしたときに、ダイアログボックスを表示させ、項目を設定したい。ダイアログボックスの仕様は以下の通り。 ・上部に項目名、データタイプ(一覧から選択)、データタイプがCharなら最大文字数、データタイプがマスターなら対象マスターを一覧から選択 ・下の部分にはすでに登録済の項目の一覧、一覧には上部の入力項目をテーブル形式で表示


ChatGPTからの回答:

目的は「入力項目設定ボタン」をクリックした際に、指定された仕様のダイアログボックスを表示させることです。

実装手順

1. ダイアログボックスのHTML構造

ダイアログボックスはモーダル形式で表示します。必要な項目を含むHTMLを作成し、CSSとJavaScriptを使用して表示・非表示を制御します。

2. JavaScriptでダイアログの動作を制御

「入力項目設定」ボタンをクリックしたときに、モーダルを表示します。また、入力内容を保持するための操作を実装します。

3. CSSでモーダルの見た目を整える

ダイアログボックスのレイアウトを設定し、背景が暗くなるようにします。


実装コード


タイトル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
<!-- タスクボックス内に含まれる入力項目設定ボタンのダイアログボックス -->
<div id="dialog" class="modal" style="display: none;">
    <div class="modal-content">
        <span class="close">×</span>
        <h2>入力項目設定</h2>
         
        <!-- 上部: 項目名、データタイプ、追加設定 -->
        <div>
            <label for="field-name">項目名:</label>
            <input type="text" id="field-name" placeholder="項目名を入力">
 
            <label for="data-type">データタイプ:</label>
            <select id="data-type">
                <option value="Char">Char</option>
                <option value="Master">Master</option>
                <option value="Integer">Integer</option>
            </select>
 
            <div id="char-settings" style="display: none;">
                <label for="max-length">最大文字数:</label>
                <input type="number" id="max-length" placeholder="文字数を入力">
            </div>
 
            <div id="master-settings" style="display: none;">
                <label for="master-list">対象マスター:</label>
                <select id="master-list">
                    <option value="Master1">Master1</option>
                    <option value="Master2">Master2</option>
                    <option value="Master3">Master3</option>
                </select>
            </div>
        </div>
 
        <!-- 下部: 登録済の項目一覧 -->
        <h3>登録済の項目一覧</h3>
        <table id="field-table" border="1">
            <thead>
                <tr>
                    <th>項目名</th>
                    <th>データタイプ</th>
                    <th>設定</th>
                </tr>
            </thead>
            <tbody>
                <!-- JavaScriptで動的に項目を追加 -->
            </tbody>
        </table>
 
        <button id="save-field">項目を追加</button>
    </div>
</div>
 
<script>
    // ダイアログボックスを制御するスクリプト
    document.addEventListener('DOMContentLoaded', function() {
        const dialog = document.getElementById('dialog');
        const closeBtn = document.querySelector('.close');
        const addFieldButton = document.getElementById('save-field');
        const dataTypeSelect = document.getElementById('data-type');
        const charSettings = document.getElementById('char-settings');
        const masterSettings = document.getElementById('master-settings');
        const fieldTableBody = document.querySelector('#field-table tbody');
 
        // 「入力項目設定」ボタンがクリックされたときにモーダルを表示
        document.querySelectorAll('.new-task-box button').forEach(button => {
            button.addEventListener('click', function() {
                dialog.style.display = 'block';
            });
        });
 
        // モーダルを閉じる
        closeBtn.addEventListener('click', function() {
            dialog.style.display = 'none';
        });
 
        // データタイプに応じて追加設定を表示
        dataTypeSelect.addEventListener('change', function() {
            const selectedType = this.value;
            charSettings.style.display = selectedType === 'Char' ? 'block' : 'none';
            masterSettings.style.display = selectedType === 'Master' ? 'block' : 'none';
        });
 
        // 項目を追加
        addFieldButton.addEventListener('click', function() {
            const fieldName = document.getElementById('field-name').value;
            const dataType = dataTypeSelect.value;
            const maxLength = document.getElementById('max-length').value;
            const masterList = document.getElementById('master-list').value;
 
            // 入力内容をテーブルに追加
            const newRow = document.createElement('tr');
            newRow.innerHTML = `
                <td>${fieldName}</td>
                <td>${dataType}</td>
                <td>${dataType === 'Char' ? `最大文字数: ${maxLength}` : dataType === 'Master' ? `対象: ${masterList}` : ''}</td>
            `;
            fieldTableBody.appendChild(newRow);
 
            // フィールドをリセット
            document.getElementById('field-name').value = '';
            dataTypeSelect.value = 'Char';
            charSettings.style.display = 'none';
            masterSettings.style.display = 'none';
        });
    });
</script>
 
<style>
    /* モーダルのスタイル */
    .modal {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.5);
        display: flex;
        justify-content: center;
        align-items: center;
    }
 
    .modal-content {
        background-color: white;
        padding: 20px;
        border-radius: 8px;
        width: 500px;
    }
 
    .close {
        position: absolute;
        top: 10px;
        right: 10px;
        cursor: pointer;
    }
</style>

仕様に基づいた機能説明

  1. 入力項目設定フォーム:

    • 項目名、データタイプを入力または選択。
    • データタイプが Char の場合は最大文字数を入力。
    • データタイプが Master の場合は対象マスターを選択。
  2. 登録済の項目一覧:

    • 入力された項目をテーブル形式で下部に表示。
  3. 動的なインタラクション:

    • JavaScriptでデータタイプに応じた追加設定の表示切り替え。
    • 項目を追加するたびにフォームをリセット。

これで、タスクごとにダイアログボックスを使って入力項目を設定する基本的な仕組みが完成します。


ChatGPTへのスクリプト:

flow_create_edit.htmlの全体のコードは?


ChatGPTからの回答:

以下は、flow_create_edit.html の全体コードです。これには、タスクボックスの「入力項目設定」ボタンをクリックするとモーダルダイアログが表示され、入力項目を設定する機能を追加したものを含みます。


完全版 flow_create_edit.html

タイトル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
{% extends '../base.html' %}
{% load static %}
{% block contents %}
<style>
    /* タスク作成エリアのスタイル */
    #task-area {
        width: 1200px;
        height: 600px;
        border: 2px solid #ccc;
        position: relative;
        overflow: auto;
        padding: 10px;
    }
 
    /* STARTとENDボックスのスタイル */
    .task-box {
        width: 150px;
        height: 50px;
        border-radius: 10px;
        background-color: #f0f0f0;
        border: 2px solid #333;
        display: flex;
        justify-content: center;
        align-items: center;
        position: absolute;
        text-align: center;
    }
 
    #start-box {
        top: 10px;
        left: 10px;
    }
 
    #end-box {
        bottom: 10px;
        right: 10px;
    }
 
    /* 新しいタスクボックスのスタイル */
    .new-task-box {
        width: 200px;
        height: 100px;
        border-radius: 10px;
        background-color: #e0f7fa;
        border: 2px solid #00796b;
        position: absolute;
        padding: 10px;
        box-sizing: border-box;
    }
 
    .new-task-box input[type="text"] {
        width: 90%;
        margin-bottom: 10px;
    }
 
    .new-task-box button {
        width: 100%;
    }
 
    /* タスクエリア全体がスクロール可能な場合のスタイル */
    #task-area::-webkit-scrollbar {
        width: 12px;
        height: 12px;
    }
 
    #task-area::-webkit-scrollbar-thumb {
        background-color: #888;
        border-radius: 10px;
    }
 
    #task-area::-webkit-scrollbar-track {
        background-color: #f0f0f0;
    }
 
    /* モーダルのスタイル */
    .modal {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.5);
        display: flex;
        justify-content: center;
        align-items: center;
    }
 
    .modal-content {
        background-color: white;
        padding: 20px;
        border-radius: 8px;
        width: 500px;
        position: relative;
    }
 
    .close {
        position: absolute;
        top: 10px;
        right: 10px;
        cursor: pointer;
    }
</style>
 
<h1>フロー作成</h1>
 
<!-- タスク作成エリア -->
<div id="task-area">
    <!-- STARTボックス -->
    <div id="start-box" class="task-box">
        START
    </div>
 
    <!-- ENDボックス -->
    <div id="end-box" class="task-box">
        END
    </div>
</div>
 
<!-- タスク作成ボタン -->
<button type="button" id="add-task-btn">タスク作成</button>
 
<!-- ダイアログボックス -->
<div id="dialog" class="modal" style="display: none;">
    <div class="modal-content">
        <span class="close">×</span>
        <h2>入力項目設定</h2>
         
        <!-- 上部: 項目名、データタイプ、追加設定 -->
        <div>
            <label for="field-name">項目名:</label>
            <input type="text" id="field-name" placeholder="項目名を入力">
 
            <label for="data-type">データタイプ:</label>
            <select id="data-type">
                <option value="Char">Char</option>
                <option value="Master">Master</option>
                <option value="Integer">Integer</option>
            </select>
 
            <div id="char-settings" style="display: none;">
                <label for="max-length">最大文字数:</label>
                <input type="number" id="max-length" placeholder="文字数を入力">
            </div>
 
            <div id="master-settings" style="display: none;">
                <label for="master-list">対象マスター:</label>
                <select id="master-list">
                    <option value="Master1">Master1</option>
                    <option value="Master2">Master2</option>
                    <option value="Master3">Master3</option>
                </select>
            </div>
        </div>
 
        <!-- 下部: 登録済の項目一覧 -->
        <h3>登録済の項目一覧</h3>
        <table id="field-table" border="1">
            <thead>
                <tr>
                    <th>項目名</th>
                    <th>データタイプ</th>
                    <th>設定</th>
                </tr>
            </thead>
            <tbody>
                <!-- JavaScriptで動的に項目を追加 -->
            </tbody>
        </table>
 
        <button id="save-field">項目を追加</button>
    </div>
</div>
 
<script>
    let taskCounter = 0;
 
    // タスク作成ボタンのクリックイベント
    document.getElementById('add-task-btn').addEventListener('click', function() {
        taskCounter += 1;
        const taskArea = document.getElementById('task-area');
 
        const newTaskBox = document.createElement('div');
        newTaskBox.className = 'new-task-box';
        newTaskBox.style.top = (50 + taskCounter * 60) + 'px';
        newTaskBox.style.left = (50 + taskCounter * 60) + 'px';
 
        const taskInput = document.createElement('input');
        taskInput.type = 'text';
        taskInput.placeholder = 'タスク名を入力';
 
        const settingsButton = document.createElement('button');
        settingsButton.textContent = '入力項目設定';
        settingsButton.addEventListener('click', function() {
            document.getElementById('dialog').style.display = 'block';
        });
 
        newTaskBox.appendChild(taskInput);
        newTaskBox.appendChild(settingsButton);
 
        taskArea.appendChild(newTaskBox);
    });
 
    // ダイアログ制御
    const dialog = document.getElementById('dialog');
    const closeBtn = document.querySelector('.close');
    const dataTypeSelect = document.getElementById('data-type');
    const charSettings = document.getElementById('char-settings');
    const masterSettings = document.getElementById('master-settings');
    const fieldTableBody = document.querySelector('#field-table tbody');
 
    closeBtn.addEventListener('click', function() {
        dialog.style.display = 'none';
    });
 
    dataTypeSelect.addEventListener('change', function() {
        charSettings.style.display = this.value === 'Char' ? 'block' : 'none';
        masterSettings.style.display = this.value === 'Master' ? 'block' : 'none';
    });
 
    document.getElementById('save-field').addEventListener('click', function() {
        const fieldName = document.getElementById('field-name').value;
        const dataType = dataTypeSelect.value;
        const maxLength = document.getElementById('max-length').value;
        const masterList = document.getElementById('master-list').value;
 
        const newRow = document.createElement('tr');
        newRow.innerHTML = `
            <td>${fieldName}</td>
            <td>${dataType}</td>
            <td>${dataType === 'Char' ? `最大文字数: ${maxLength}` : dataType === 'Master' ? `対象: ${masterList}` : ''}</td>
        `;
        fieldTableBody.appendChild(newRow);
 
        document.getElementById('field-name').value = '';
        dataTypeSelect.value = 'Char';
        charSettings.style.display = 'none';
        masterSettings.style.display = 'none';
    });
</script>
{% endblock %}

このコードで、タスクボックスの「入力項目設定」ボタンをクリックすると、仕様に基づくモーダルダイアログが表示され、項目を追加することが可能になります。


このコードを実装後、「入力項目設定」ボタンをクリックすると、以下のような入力ボックスが表示されます。






次回はこの入力ボックスをアレンジしていきたいと思います。

2024-12-11

ChatGPTを使って、アプリ開発(ワークフロー5):開発3

 表示される画面の装飾を行います。ただし、今後作成する画面に毎回同じ装飾設定をするのは、面倒なので、基本となる画面デザインを使いまわす方法をとります。

1.基本となる画面作成

 ①「base.html」というファイルを作成します。

   「templates」で右クリックし、「新規」→「HTMLファイル」を選択します。






②「base」と入力し、base.htmlを作成します。




③base.htmlの中身をすべて削除し、以下のコードに書き換えます。

タイトル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<!DOCTYPE html>
<html lang="ja">
 
{% load static %}
<head>
<title>workflow Tools</title>
    <link rel="stylesheet" type="text/css" href="{% static 'css/base.css' %}">
</head>
<style>
.side-bar{
    width: 160px;
    height: auto;
    background: #b0c4de;
    padding-top : 5px;
}
.sidebar-list{
    list-style: none;
    margin: 10px 0px 0px -35px;
}
a   {
    text-decoration:none;
}
a:hover {
    text-decoration: underline;
    color: #ffffe0;
}
</style>
<body class="dashboard"
  data-admin-utc-offset="32400">
    <!-- Container -->
        <div id="header">
            <div id="branding">
                <h1 id="site-name"><a href="/wf/">ワークフロートップページ</a></h1>
            </div>
            {% if user.is_authenticated %}
                <div id="user-tools">
                    <div>ようこそ {{user.last_name}} {{user.first_name}} さん /
                        <a href="/wf/">ワークフロートップページ</a> /
                        <a href="{% url 'admin:password_change' %}">パスワードの変更</a> /
                        <a href="/accounts/logout/">ログアウト</a>
                    </div>
                </div>
            {% endif %}
        </div>
        <!-- END Header -->
        <!-- Content -->
        <div class="container-fluid">
            <div class="row">
                <div style="float:left;width:170px">
                    <div class="side-bar" style="height:93.5vh">
                        <div class="col-sm-3 col-md-2 sidebar">
                            {% if user.is_authenticated %}
                                <ul class="nav nav-sidebar">
                                    <li class="sidebar-list"><a href="/admin/">システム管理</a></li>
                                </ul>
                            {% endif %}
                        </div>
                    </div>
                </div>
                <div class="contents_area" style="float:left;">
                    {% block contents %}{% endblock %}
                </div>
            </div>
        </div>
</body>
</html>

・{% load static %}の部分で、「static」ディレクトリのファイルを読み込めるようにします。

・下から6行目の{% block contents %}{% endblock %}の部分が、実際に内容を表示させる部分になります。

・<style>にて、サイドバーの装飾を記述しています。

④装飾用のCSSファイル(base.css)は「static」→「admin」→「css」のbase.cssファイルを「static」→「css」にコピーします。

 ・コピー元のファイルで右クリックし、「コピー」を選択します。











 ・コピー先のディレクトリで右クリックし、「貼り付け」を選択します。














・名前は変更せず、「OK」をクリックします。








2.以前作成したflow_list.htmlを以下のように変更します。

タイトル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
{% extends '../base.html' %}
{% load static %}
{% block contents %}
    <h1>フロー一覧</h1>
 
    <!-- フローの新規作成ボタン -->
    <a href="{% url 'flow_create' %}" class="btn btn-primary">新規作成</a>
 
    <!-- ユーザーが作成したフローの一覧 -->
    {% if flows %}
        <table class="table">
            <thead>
                <tr>
                    <th>フロー名</th>
                    <th>作成日</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                {% for flow in flows %}
                    <tr>
                        <td>{{ flow.name }}</td>
                        <td>{{ flow.created_at|date:"Y/m/d H:i" }}</td>
                        <td>
                            <!-- 編集ボタン -->
                            <a href="{% url 'flow_edit' flow.id %}" class="btn btn-warning">編集</a>
 
                            <!-- 削除ボタン -->
                            <form action="{% url 'flow_delete' flow.id %}" method="post" style="display:inline;">
                                {% csrf_token %}
                                <button type="submit" class="btn btn-danger" onclick="return confirm('本当に削除しますか?');">削除</button>
                            </form>
                        </td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    {% else %}
        <p>フローがまだ作成されていません。</p>
    {% endif %}
 
{% endblock %}


以上の設定で、以下のような画面になります。










 では、ここからフローを作成する部分を作っていきたいと思います。

構想として、以下のようにします。

・フローは視覚的に作成できるようにする。

・条件分岐や並行処理ができるようにする。

フォローの中で記録したり、閲覧する項目も自由に設定できるようにする。

ChatGPTへのスクリプト:

workflowを作成/編集する部分は?


ChatGPTからの回答:

ワークフローを作成・編集するためのページのテンプレートでは、ユーザーがタスクを追加、編集、そしてフロー全体を定義できるようにします。以下は、フロー作成・編集ページのテンプレートの例です。

フロー作成・編集ページのテンプレート (flow_create_edit.html)


タイトル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>{% if flow %}フロー編集{% else %}フロー作成{% endif %}</title>
    <link rel="stylesheet" href="{% static 'css/styles.css' %}">
    <script src="{% static 'js/flow_editor.js' %}"></script> <!-- jsPlumb等のライブラリを読み込む -->
</head>
<body>
    <h1>{% if flow %}フロー編集{% else %}フロー作成{% endif %}</h1>
 
    <!-- フロー名入力フォーム -->
    <form method="post" action="{% if flow %}{% url 'flow_edit' flow.id %}{% else %}{% url 'flow_create' %}{% endif %}">
        {% csrf_token %}
        <label for="flow_name">フロー名</label>
        <input type="text" id="flow_name" name="name" value="{% if flow %}{{ flow.name }}{% endif %}" required>
 
        <!-- タスク追加エリア -->
        <h2>タスク</h2>
        <div id="task-container">
            <!-- タスクを表示するエリア -->
            {% if flow and flow.tasks.exists %}
                {% for task in flow.tasks.all %}
                    <div class="task-box" id="task-{{ task.id }}">
                        <h3>{{ task.name }}</h3>
                        <p>タスク処理者: {{ task.assigned_user }}</p>
                        <button type="button" class="edit-task" data-task-id="{{ task.id }}">タスク編集</button>
                        <button type="button" class="delete-task" data-task-id="{{ task.id }}">タスク削除</button>
                    </div>
                {% endfor %}
            {% else %}
                <!-- 初期状態で START と END タスクを表示 -->
                <div class="task-box" id="task-start">
                    <h3>START</h3>
                </div>
                <div class="task-box" id="task-end">
                    <h3>END</h3>
                </div>
            {% endif %}
        </div>
 
        <!-- タスク作成ボタン -->
        <button type="button" id="add-task-btn">タスク作成</button>
 
        <!-- タスク編集/入力項目設定ポップアップ -->
        <div id="task-modal" style="display:none;">
            <h3>タスク設定</h3>
            <label for="task-name">タスク名</label>
            <input type="text" id="task-name" name="task-name">
             
            <h4>入力項目設定</h4>
            <div id="input-fields-container">
                <!-- ここに動的に入力項目が追加される -->
            </div>
            <button type="button" id="add-input-field-btn">入力項目追加</button>
             
            <button type="button" id="save-task-btn">タスク保存</button>
            <button type="button" id="cancel-task-btn">キャンセル</button>
        </div>
 
        <!-- フローの保存 -->
        <button type="submit">登録</button>
    </form>
 
    <!-- 削除ボタン(フロー編集時のみ表示) -->
    {% if flow %}
        <form method="post" action="{% url 'flow_delete' flow.id %}" style="margin-top: 20px;">
            {% csrf_token %}
            <button type="submit" class="btn btn-danger" onclick="return confirm('本当に削除しますか?');">削除</button>
        </form>
    {% endif %}
 
    <script>
        // タスク作成や編集、削除のためのJavaScript
        document.getElementById('add-task-btn').addEventListener('click', function() {
            // タスク作成ロジック
            // 例えば、タスクボックスを追加するコードをここに記述
        });
 
        document.querySelectorAll('.edit-task').forEach(function(button) {
            button.addEventListener('click', function() {
                const taskId = button.getAttribute('data-task-id');
                // タスク編集ロジック(モーダルを開くなど)
            });
        });
 
        document.querySelectorAll('.delete-task').forEach(function(button) {
            button.addEventListener('click', function() {
                const taskId = button.getAttribute('data-task-id');
                // タスク削除ロジック
            });
        });
    </script>
</body>
</html>


説明

  1. フロー名の入力: フロー名を入力できるテキストフィールドがあり、編集時には既存のフロー名が表示されます。

  2. タスク作成ボタン: ユーザーが新しいタスクを追加できるボタン。クリックすると、タスクを作成するためのモーダルウィンドウが表示され、タスクの名前や入力項目を設定できます。

  3. タスク表示エリア: 既存のタスク(編集時の場合)を表示し、タスクの編集や削除ができるようにしています。また、フロー作成時は「START」と「END」のタスクボックスをデフォルトで表示します。

  4. タスク編集用モーダル: ユーザーがタスクを作成または編集する際に、入力項目やタスク名を設定するためのポップアップウィンドウ。

  5. JavaScript部分: タスクの追加、編集、削除などの動的操作を行うために、JavaScriptを使用しています。このコードではイベントリスナーを設定し、ボタンをクリックするとタスクを追加したり、既存のタスクを編集するモーダルを開いたりすることができます。

  6. フローの保存: フォームが送信されると、フローのデータが保存されます。編集時にはフローの削除ボタンも表示されます。

補足

  • このテンプレートには、jsPlumbDrag and Dropのようなタスク同士を結ぶ機能を追加するためのJavaScriptライブラリも組み込むことが可能です。
  • タスク作成、編集、削除の処理はバックエンド側でハンドリングする必要がありますので、各機能に対応するビューとロジックが必要です。

base.htmlを継承するように、以下のように変更する。


タイトル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
{% extends '../base.html' %}
{% load static %}
{% block contents %}
    <h1>{% if flow %}フロー編集{% else %}フロー作成{% endif %}</h1>
 
    <!-- フロー名入力フォーム -->
    <form method="post" action="{% if flow %}{% url 'flow_edit' flow.id %}{% else %}{% url 'flow_create' %}{% endif %}">
        {% csrf_token %}
        <label for="flow_name">フロー名</label>
        <input type="text" id="flow_name" name="name" value="{% if flow %}{{ flow.name }}{% endif %}" required>
 
        <!-- タスク追加エリア -->
        <h2>タスク</h2>
        <div id="task-container">
            <!-- タスクを表示するエリア -->
            {% if flow and flow.tasks.exists %}
                {% for task in flow.tasks.all %}
                    <div class="task-box" id="task-{{ task.id }}">
                        <h3>{{ task.name }}</h3>
                        <p>タスク処理者: {{ task.assigned_user }}</p>
                        <button type="button" class="edit-task" data-task-id="{{ task.id }}">タスク編集</button>
                        <button type="button" class="delete-task" data-task-id="{{ task.id }}">タスク削除</button>
                    </div>
                {% endfor %}
            {% else %}
                <!-- 初期状態で START と END タスクを表示 -->
                <div class="task-box" id="task-start">
                    <h3>START</h3>
                </div>
                <div class="task-box" id="task-end">
                    <h3>END</h3>
                </div>
            {% endif %}
        </div>
 
        <!-- タスク作成ボタン -->
        <button type="button" id="add-task-btn">タスク作成</button>
 
        <!-- タスク編集/入力項目設定ポップアップ -->
        <div id="task-modal" style="display:none;">
            <h3>タスク設定</h3>
            <label for="task-name">タスク名</label>
            <input type="text" id="task-name" name="task-name">
 
            <h4>入力項目設定</h4>
            <div id="input-fields-container">
                <!-- ここに動的に入力項目が追加される -->
            </div>
            <button type="button" id="add-input-field-btn">入力項目追加</button>
 
            <button type="button" id="save-task-btn">タスク保存</button>
            <button type="button" id="cancel-task-btn">キャンセル</button>
        </div>
 
        <!-- フローの保存 -->
        <button type="submit">登録</button>
    </form>
 
    <!-- 削除ボタン(フロー編集時のみ表示) -->
    {% if flow %}
        <form method="post" action="{% url 'flow_delete' flow.id %}" style="margin-top: 20px;">
            {% csrf_token %}
            <button type="submit" class="btn btn-danger" onclick="return confirm('本当に削除しますか?');">削除</button>
        </form>
    {% endif %}
 
    <script>
        // タスク作成や編集、削除のためのJavaScript
        document.getElementById('add-task-btn').addEventListener('click', function() {
            // タスク作成ロジック
            // 例えば、タスクボックスを追加するコードをここに記述
        });
 
        document.querySelectorAll('.edit-task').forEach(function(button) {
            button.addEventListener('click', function() {
                const taskId = button.getAttribute('data-task-id');
                // タスク編集ロジック(モーダルを開くなど)
            });
        });
 
        document.querySelectorAll('.delete-task').forEach(function(button) {
            button.addEventListener('click', function() {
                const taskId = button.getAttribute('data-task-id');
                // タスク削除ロジック
            });
        });
    </script>
{% endblock %}


ChatGPTへのスクリプト:

ビューは?


ChatGPTからの回答:

フロー作成・編集ページのためのDjangoビューを実装するには、以下のようにCreateViewUpdateViewを使って、新規作成と編集機能を提供することができます。タスクの動的な追加・削除に関しては、Ajaxを使用してサーバーと非同期通信を行うと、ユーザー体験がスムーズになります。

以下では、フローの作成・編集、タスクの追加・編集、および削除に対応するDjangoビューの例を示します。

1. フロー作成ビュー

まず、flow_createflow_editに対応するビューを作成します。

タイトル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from .models import Flow, Task
from .forms import FlowForm, TaskForm
 
# フローの作成
@login_required
def flow_create(request):
    if request.method == 'POST':
        form = FlowForm(request.POST)
        if form.is_valid():
            flow = form.save(commit=False)
            flow.creator = request.user
            flow.save()
            # STARTとENDタスクを自動作成
            Task.objects.create(flow=flow, name='START')
            Task.objects.create(flow=flow, name='END')
            return redirect('flow_edit', flow.id)
    else:
        form = FlowForm()
     
    return render(request, 'workflow/flow_create_edit.html', {'form': form, 'flow': None})
 
# フローの編集
@login_required
def flow_edit(request, flow_id):
    flow = get_object_or_404(Flow, id=flow_id, creator=request.user)
     
    if request.method == 'POST':
        form = FlowForm(request.POST, instance=flow)
        if form.is_valid():
            form.save()
            return redirect('flow_edit', flow.id)
    else:
        form = FlowForm(instance=flow)
     
    tasks = flow.tasks.all()
     
    return render(request, 'workflow/flow_create_edit.html', {'form': form, 'flow': flow, 'tasks': tasks})


2. タスク追加・編集ビュー

タスクはフロー内で動的に追加されるため、Ajaxを使って非同期に追加・編集することが推奨されます。以下は、タスク追加と編集をAjax経由で行うビューの例です。

タイトル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# タスクの追加
@login_required
def add_task(request, flow_id):
    flow = get_object_or_404(Flow, id=flow_id, creator=request.user)
     
    if request.method == 'POST':
        form = TaskForm(request.POST)
        if form.is_valid():
            task = form.save(commit=False)
            task.flow = flow
            task.save()
            return JsonResponse({'task_id': task.id, 'task_name': task.name}, status=201)
     
    return JsonResponse({'error': 'Invalid data'}, status=400)
 
# タスクの編集
@login_required
def edit_task(request, task_id):
    task = get_object_or_404(Task, id=task_id, flow__creator=request.user)
     
    if request.method == 'POST':
        form = TaskForm(request.POST, instance=task)
        if form.is_valid():
            form.save()
            return JsonResponse({'task_id': task.id, 'task_name': task.name}, status=200)
     
    return JsonResponse({'error': 'Invalid data'}, status=400)

3. タスク削除ビュー

タスクの削除もAjaxで非同期に行う場合の例です。

タイトル
1
2
3
4
5
6
7
8
9
10
# タスクの削除
@login_required
def delete_task(request, task_id):
    task = get_object_or_404(Task, id=task_id, flow__creator=request.user)
     
    if request.method == 'POST':
        task.delete()
        return JsonResponse({'message': 'Task deleted'}, status=200)
     
    return JsonResponse({'error': 'Failed to delete task'}, status=400)


4. フォーム

タスクやフローのデータを操作するために、forms.pyでフォームを定義します。

タイトル
1
2
3
4
5
6
7
8
9
10
11
12
from django import forms
from .models import Flow, Task
 
class FlowForm(forms.ModelForm):
    class Meta:
        model = Flow
        fields = ['name']
 
class TaskForm(forms.ModelForm):
    class Meta:
        model = Task
        fields = ['name', 'assigned_user']

5. URL設定

これらのビューにアクセスできるように、urls.pyでルーティングを設定します。

タイトル
1
2
3
4
5
6
7
8
9
10
from django.urls import path
from . import views
 
urlpatterns = [
    path('flows/create/', views.flow_create, name='flow_create'),
    path('flows/<int:flow_id>/edit/', views.flow_edit, name='flow_edit'),
    path('flows/<int:flow_id>/add_task/', views.add_task, name='add_task'),
    path('tasks/<int:task_id>/edit/', views.edit_task, name='edit_task'),
    path('tasks/<int:task_id>/delete/', views.delete_task, name='delete_task'),
]

6. Ajaxのフロントエンド操作

JavaScriptを使用して、非同期でタスクを追加・編集・削除するコードを用意します。例えば、flow_create_edit.htmlのテンプレート内にJavaScriptを含めることで、動的にタスクを操作できます。

タイトル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
document.getElementById('add-task-btn').addEventListener('click', function() {
    const taskName = prompt("タスク名を入力してください");
    if (taskName) {
        fetch(`/flows/${flowId}/add_task/`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRFToken': getCookie('csrftoken')
            },
            body: JSON.stringify({ name: taskName })
        })
        .then(response => response.json())
        .then(data => {
            if (data.task_id) {
                // 新しいタスクをDOMに追加する処理
                const taskContainer = document.getElementById('task-container');
                taskContainer.innerHTML += `<div class="task-box" id="task-${data.task_id}"><h3>${data.task_name}</h3></div>`;
            }
        });
    }
});

まとめ

  • flow_createおよびflow_editビューでは、ユーザーがフローを作成・編集するロジックを実装します。
  • タスクの追加・編集・削除はAjaxを利用して非同期で行い、ページのリロードを最小限に抑え、ユーザー体験を向上させます。
  • フロントエンド側でJavaScriptを使い、タスクの動的操作を実現し、サーバー側ではDjangoのビューを使用してデータベースと連携します。

これにより、Djangoでのワークフロー作成・編集機能が実装できます。

次回から、開発環境サーバーで実行確認しながら、さらにブラシュアップしていきます。

2024-11-30

ChatGPTを使って、アプリ開発(ワークフロー)4:開発2

  開発環境用のWebサーバーを起動させて確認するため、まずはDBにテーブルを作成していきます。

 とはいっても、Pycharmのターミナルから操作していきます。

1.「python manage.py makemigrations wf」コマンドを実行します。







2.「python manage.py migrate」コマンドを実行し、DBにテーブルを作成します。










3.Webブラウザでのアクセスに対する処理を設定します。

 ①「mysite」の「urls.py」を以下のように設定します。


タイトル
1
2
3
4
5
6
7
8
from django.contrib import admin
from django.urls import path, include
 
urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('django.contrib.auth.urls')),
    path('wf/', include('wf.urls')),
]


 ②「wf」を右クリックし、「新規」→「Pythonファイル」をクリックします。






 ③「urls.py」と入力し、「Enter」を押して、ファイルを作成します。







 ④作成した「urls.py」を以下のように設定します。

タイトル
1
2
3
4
5
6
from django.urls import path
from . import views
 
urlpatterns = [
    path('', views.flow_list, name='flow_list'),  # トップページ
]



4.フロー一覧ビューのテンプレートを作成します。

 ①「templates」を右クリックし、「新規」→「ディレクトリ」をクリックします。







 ②「workflow」と入力し、「Enter」を押して、ファイルを作成します。




 ③「workflow」を右クリックし、「新規」→「HTML ファイル」をクリックします。







 ④「flow_list.html」と入力し、「Enter」を押して、ファイルを作成します。




 ⑤「flow_list.html」の中身はChatGPTから回答のあった以下のコードを張り付けます。


タイトル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>フロー一覧</title>
    <link rel="stylesheet" href="{% static 'css/styles.css' %}">
</head>
<body>
    <h1>フロー一覧</h1>
 
    <!-- フローの新規作成ボタン -->
    <a href="{% url 'flow_create' %}" class="btn btn-primary">新規作成</a>
 
    <!-- ユーザーが作成したフローの一覧 -->
    {% if flows %}
        <table class="table">
            <thead>
                <tr>
                    <th>フロー名</th>
                    <th>作成日</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                {% for flow in flows %}
                    <tr>
                        <td>{{ flow.name }}</td>
                        <td>{{ flow.created_at|date:"Y/m/d H:i" }}</td>
                        <td>
                            <!-- 編集ボタン -->
                            <a href="{% url 'flow_edit' flow.id %}" class="btn btn-warning">編集</a>
 
                            <!-- 削除ボタン -->
                            <form action="{% url 'flow_delete' flow.id %}" method="post" style="display:inline;">
                                {% csrf_token %}
                                <button type="submit" class="btn btn-danger" onclick="return confirm('本当に削除しますか?');">削除</button>
                            </form>
                        </td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    {% else %}
        <p>フローがまだ作成されていません。</p>
    {% endif %}
</body>
</html>


5.管理権限を持ったユーザー(スーパーユーザー)を登録します。

 ※今回は、「admin」で登録します。

 「python manage.py createsuperuser」を実行し、ユーザーID、メールアドレス、パスワード、パスワード(確認)を入力します。







6.開発環境用のWebサーバーを起動させます。








7.Webブラウザのアドレス欄に「http://127.0.0.1:8000/admin/」と入力し、アクセスします。先ほど登録したユーザーのIDとパスワードを入力し、「ログイン」をクリックします。









8.以下の画面が表示されたらログインできています。





9.Webブラウザのアドレス欄に「http://127.0.0.1:8000/wf/」と入力し、作成した「flow_list.html」画面を開きます。





この時、エラーが発生しました。7行目のところで、「Invalid block tag on line 7: 'static'. Did you forget to register or load this tag?」というエラーが出ています。


10.ChatGPTに聞いてみます。



ChatGPTへのスクリプト:

テンプレート開いたときに「<link rel="stylesheet" href="{% static 'css/styles.css' %}">」の部分で、「Invalid block tag on line 7: 'static'. Did you forget to register or load this tag?」というエラーが出た。原因と対処は?


ChatGPTからの回答:

エラーの原因は、テンプレートで{% static %}タグを使用する際に必要なstaticテンプレートタグライブラリがロードされていないためです。このタグを使用するには、テンプレート内で最初に{% load static %}を記述する必要があります。

修正方法は以下の通りです。

修正後のテンプレート

タイトル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!DOCTYPE html>
<html lang="ja">
 
<head>
    <meta charset="UTF-8">
    <title>フロー一覧</title>
    {% load static %}
    <link rel="stylesheet" href="{% static 'css/styles.css' %}">
</head>
<body>
    <h1>フロー一覧</h1>
 
    <!-- フローの新規作成ボタン -->
    <a href="{% url 'flow_create' %}" class="btn btn-primary">新規作成</a>
 
    <!-- ユーザーが作成したフローの一覧 -->
    {% if flows %}
        <table class="table">
            <thead>
                <tr>
                    <th>フロー名</th>
                    <th>作成日</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                {% for flow in flows %}
                    <tr>
                        <td>{{ flow.name }}</td>
                        <td>{{ flow.created_at|date:"Y/m/d H:i" }}</td>
                        <td>
                            <!-- 編集ボタン -->
                            <a href="{% url 'flow_edit' flow.id %}" class="btn btn-warning">編集</a>
 
                            <!-- 削除ボタン -->
                            <form action="{% url 'flow_delete' flow.id %}" method="post" style="display:inline;">
                                {% csrf_token %}
                                <button type="submit" class="btn btn-danger" onclick="return confirm('本当に削除しますか?');">削除</button>
                            </form>
                        </td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    {% else %}
        <p>フローがまだ作成されていません。</p>
    {% endif %}
</body>
</html>

修正ポイント

  • <head>内の<link rel="stylesheet" href="{% static 'css/styles.css' %}">を正しく動作させるために、**{% load static %}**を追加しました。

他の確認ポイント

  1. Djangoのstaticfiles設定が有効か確認:

    • settings.pyINSTALLED_APPS'django.contrib.staticfiles'が含まれていることを確認してください。
    • STATIC_URLが正しく設定されているか確認してください。
  2. css/styles.cssの存在確認:

    • ファイルパスが正しいか確認し、DjangoのSTATICFILES_DIRSstaticディレクトリに正しく配置されているかを確認します。

コマンド実行(静的ファイルの収集が必要な場合)


開発環境では不要ですが、本番環境では以下を実行して静的ファイルを収集します:

「python manage.py collectstatic」



11.「flow_list.html」を修正し、「python manage.py collectstatic」を実行します。
  ※コマンド実行には、「Ctrl + C」でテストサーバーをとめる必要があります。
   コマンド実行後は再度、テストサーバーを立ち上げます。







12.再び、「http://127.0.0.1:8000/wf/」にアクセスしますが、以下のように14行目の「<a href="{% url 'flow_create' %}" class="btn btn-primary">新規作成</a>」の部分で、「Reverse for 'flow_create' not found. 'flow_create' is not a valid view function or pattern name.」というエラーが出ています。











ChatGPTへのスクリプト:

「<a href="{% url 'flow_create' %}" class="btn btn-primary">新規作成</a>」の部分で、「Reverse for 'flow_create' not found. 'flow_create' is not a valid view function or pattern name.」というエラーが出る。


ChatGPTからの回答:

エラーの原因は、flow_createという名前付きURLパターンがurls.pyに定義されていないためです。テンプレートで{% url 'flow_create' %}を使用するには、対応するURLパターンをurls.pyに定義し、その名前をflow_createにする必要があります。

以下に修正方法を示します。

修正方法

1. views.pyflow_createビューを追加

タイトル
1
2
3
4
5
6
7
8
9
from django.shortcuts import render, redirect
 
# フロー作成ビュー
def flow_create(request):
    if request.method == 'POST':
        # フォームの処理を追加
        # 新しいフローを作成し保存する
        return redirect('flow_list')
    return render(request, 'flow_create.html')

2. urls.pyflow_createを追加

タイトル
1
2
3
4
5
6
7
from django.urls import path
from . import views
 
urlpatterns = [
    path('', views.flow_list, name='flow_list'),  # トップページ
    path('create/', views.flow_create, name='flow_create'),  # フロー作成

3. flow_create.htmlテンプレートを作成

タイトル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>フロー作成</title>
</head>
<body>
    <h1>新しいフローを作成</h1>
    <form method="post">
        {% csrf_token %}
        <label for="name">フロー名:</label>
        <input type="text" name="name" id="name" required>
        <button type="submit">作成</button>
    </form>
    <a href="{% url 'flow_list' %}">戻る</a>
</body>
</html>

追加で確認するポイント

  • URLの変更に伴う影響:
    • 既存コードやリンクでflow_createを参照している箇所がないか確認し、必要に応じて修正してください。
  • テンプレートに適切なエラーメッセージやバリデーションを追加:
    • 作成フォームの入力エラーメッセージなどを処理できるようにしましょう。

これでflow_createのURLが正しく動作し、エラーが解消するはずです。


ChatGPTの回答に基づいて、コードの追加、テンプレートの追加を行い、再び、「http://127.0.0.1:8000/wf/」にアクセスすると、以下の画面が表示されます。











今日はここまでにします。

次からは、フローを作成する部分の開発を進めます。なお、画面の見た目がいまいちなので、最初にそのあたりの修正を行います。

2024-11-24

ChatGPTを使って、アプリ開発(ワークフロー)3:開発1

  これから、ChatGPTと会話をしながら、開発を進めていきたいと思います。

 ChatGPTへのスクリプトは青文字で、ChatGPTからの回答を赤文字で記載していきます。


 まずは全体構想を伝えます。

ChatGPTへのスクリプト:

Djangoでワークフローツールを作成したい。 

基本構想は以下の通り。

 1.ユーザーがフローを作成する

 2.作成したフローをユーザーが使用する。

 詳細は以下のとおりとする

 〇フロー作成部分

 1.ユーザーがフロー作成/編集ページjにアクセスする。

 2.ユーザー本人が作成したフローに一覧が表示される

 3a.一覧のフローを選択   →フロー作成ページに遷移し、選択したフローを表示

 4.「更新」ボタンをクリック   →変更があれば保存

 5.「削除」ボタンをクリックする   →「本当に削除しますか?」とメッセージを出す 

 6.「OK」をクリック   →フローデータを削除

 3b・「新規作成」ボタンをクリック   →フロー作成ページに遷移する。タスクを作成するエリアには、「START」と「END」のボックスをデフォルトで表示する

 2.フロー名を入力

 3.タスクを作成する 

  ①「タスク作成」をクリック

    →四角いボックスが画面上に作成される。

  ②タスク名をボックス内の入力欄に入力

  ③ボックス内の「入力項目設定」ボタンをクリック

    →ポップアップが開く

  ④項目名(日本語)の入力と、データ型の選択および必須かどうかの選択を行う。データが文字型の場合、文字数の入力も行う。

  ⑤「作成」ボタンをクリックする

    →すでに登録済の入力項目がある場合はその下に、ない場合は「作成」ボタンの下に追加せれる。この時、項目名の左に「削除」ボタンを配置し、「削除」ボタンをクリックしたら項目を削除する

  ⑥さらに項目を追加する場合は、④~⑤を繰り返す。

  ⑦このタスクの処理をする人を個人、あるいは、別途マスタで設定したクラスで設定する

 4.フローを作成する

  ①作成されたタスクを→で結ぶ 

  ②分岐、並列処理もできるようにそれぞれを記号で表現する。

  ③並列処理の場合、合流記号で合流させる

  ④並列処理の場合、並列処理すべての完了を待つか、いずれか1つでも完了したら次に進むかを設定する

 5.「登録」ボタンをクリック

    →フローが保存され、「home」ページに遷移

 6.作成済フロー一覧を選択

 7. 8.一覧のフローを選択

   →フロー作成ページに遷移し、選択したフローを表示

 9.「更新」ボタンをクリック

   →変更があれば保存

 10.「削除」ボタンをクリックする

   →「本当に削除しますか?」とメッセージを出す

 11.「OK」をクリック

   →フローデータを削除


 〇フローの使用

 1.ユーザーが「ワークフロー」を選択

  →ユーザーが使用可能な一覧のページに遷移

 2.フローを選択する

  →登録されている入力項目を表示する。

 3.入力欄に入力

 4.次の処理者が個人指定されていない場合は、次の処理者を選択する

 5.「実行」ボタンをクリック

 6.処理者と処理日時を記録し、次の処理者へ回す。


ChatGPTからの回答:

Djangoを使用して、ワークフローツールの作成構想はしっかりしていますね。このプロジェクトを実装するためには、以下のステップで進めることが考えられます。

1. プロジェクトのセットアップ

  • Djangoプロジェクトを作成し、必要なアプリケーション(例: workflow、tasks)を設定します。
  • フローデータを管理するためのモデルを設計します。たとえば、「Flow」「Task」「InputField」「UserAction」などのモデルを定義します。

2. モデルの設計

以下は、想定されるモデルの概要です。

Flowモデル

タイトル
1
2
3
4
5
6
7
8
from django.db import models
from django.contrib.auth.models import User
 
class Flow(models.Model):
    name = models.CharField(max_length=100)
    creator = models.ForeignKey(User, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)


Taskモデル

タイトル
1
2
3
4
5
6
from django.db import models
class Task(models.Model):
    flow = models.ForeignKey(Flow, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    order = models.IntegerField()  # タスクの順序を管理するフィールド
    next_tasks = models.ManyToManyField('self', symmetrical=False)


InputFieldモデル

タイトル
1
2
3
4
5
6
7
rom django.db import models
class InputField(models.Model):
    task = models.ForeignKey(Task, on_delete=models.CASCADE)
    field_name = models.CharField(max_length=100)
    field_type = models.CharField(max_length=50, choices=[('text', 'Text'), ('number', 'Number')])
    required = models.BooleanField(default=False)
    max_length = models.IntegerField(null=True, blank=True)


UserActionモデル

タイトル
1
2
3
4
5
from django.db import models
class UserAction(models.Model):
    task = models.ForeignKey(Task, on_delete=models.CASCADE)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    completed_at = models.DateTimeField(null=True, blank=True)



3. ビューとテンプレート

ユーザーがフローを作成・編集するためのビューや、フロー一覧表示、タスク作成ページのビューを作成します。

フロー一覧ビュー

タイトル
1
2
3
4
5
6
from django.shortcuts import render
from .models import Flow
 
def flow_list(request):
    flows = Flow.objects.filter(creator=request.user)
    return render(request, 'workflow/flow_list.html', {'flows': flows})



タスク作成ページのビュー

タイトル
1
2
3
4
5
6
7
8
9
from django.shortcuts import render, get_object_or_404
from .models import Flow, Task
 
def task_create(request, flow_id):
    flow = get_object_or_404(Flow, id=flow_id)
    if request.method == 'POST':
        # タスク作成ロジック
        pass
    return render(request, 'workflow/task_create.html', {'flow': flow})



4. JavaScriptでの動的操作

タスクのドラッグ&ドロップや、分岐・並列処理の結合を表現するために、フロントエンド側でJavaScriptを使用して、動的なUIを実装します。

たとえば、ドラッグ&ドロップの機能を追加するために「jsPlumb」などのライブラリを使用するのが考えられます。

5. ユーザーのフロー実行

実行時にフローに沿って入力項目を表示し、データを送信・次の処理者へ渡すためのロジックを実装します。

実行ページのビュー

タイトル
1
2
3
4
5
6
7
def execute_flow(request, flow_id):
    flow = get_object_or_404(Flow, id=flow_id)
    tasks = flow.task_set.all()  # タスクの一覧を取得
    if request.method == 'POST':
        # 入力データを受け取り処理
        pass
    return render(request, 'workflow/execute_flow.html', {'flow': flow, 'tasks': tasks})



6. ワークフローの状態管理

タスクの進捗状況や次の処理者を追跡するため、各タスクの状態を「完了」「保留」などで管理し、次の処理に適切に引き継ぎます。

7. フローデータの保存と削除

フローを保存・更新する場合や、削除時の確認メッセージはDjangoのフォームやJavaScriptで実装します。

8. テストとデプロイ

すべての機能をテストし、Djangoサーバーで動作確認後、HerokuやAWSなどでデプロイを行います。

上記の流れを基本に、詳細設計を進めていくと良いでしょう。


ChatGPTへのスクリプト:

フロー一覧ビューのtemplateのコードは?


ChatGPTからの回答:

フロー一覧ビューのテンプレートコードは、ユーザーが作成したフローのリストを表示し、それぞれのフローに対して編集、削除、新規作成ボタンを提供する形式にできます。以下はその例です。

フロー一覧ビューのテンプレート (flow_list.html)


タイトル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>フロー一覧</title>
    <link rel="stylesheet" href="{% static 'css/styles.css' %}">
</head>
<body>
    <h1>フロー一覧</h1>
 
    <!-- フローの新規作成ボタン -->
    <a href="{% url 'flow_create' %}" class="btn btn-primary">新規作成</a>
 
    <!-- ユーザーが作成したフローの一覧 -->
    {% if flows %}
        <table class="table">
            <thead>
                <tr>
                    <th>フロー名</th>
                    <th>作成日</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                {% for flow in flows %}
                    <tr>
                        <td>{{ flow.name }}</td>
                        <td>{{ flow.created_at|date:"Y/m/d H:i" }}</td>
                        <td>
                            <!-- 編集ボタン -->
                            <a href="{% url 'flow_edit' flow.id %}" class="btn btn-warning">編集</a>
 
                            <!-- 削除ボタン -->
                            <form action="{% url 'flow_delete' flow.id %}" method="post" style="display:inline;">
                                {% csrf_token %}
                                <button type="submit" class="btn btn-danger" onclick="return confirm('本当に削除しますか?');">削除</button>
                            </form>
                        </td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    {% else %}
        <p>フローがまだ作成されていません。</p>
    {% endif %}
</body>
</html>



説明

  • {% for flow in flows %}: これは、Djangoテンプレートの繰り返し構文で、flows変数内のフローを1つずつ表示しています。
  • フロー名と作成日: フローの名前と作成日をそれぞれのテーブル列に表示しています。
  • 編集ボタン: flow_editビューへのリンクを提供し、クリックで選択したフローの編集ページに遷移します。
  • 削除ボタン: 削除時に確認メッセージを表示し、ユーザーが確認したら削除アクションを実行します。

このテンプレートは、シンプルなリスト表示で、ユーザーがフローを編集・削除・新規作成できるように構成されています。



 ChatGPTからの回答をもとに、開発作業を進めます。実際には、回答で得たコードを実際にコーディング(というか、コピペ)して、動作確認をしながら、追加の要望をChatGPTに投げかけて、追加、改造していく形になります。

 次回から実際に開発環境用のWebサーバーを起動させて、コードの動作確認を行いながら、アプリをブラシュアップしていきます。





2024-11-23

ChatGPTを使って、アプリ開発(ワークフロー)2:環境構築

  あて、アプリを開発するにあたっては、pythonのDjangoというWebアプリフレームワークを使っていきます。統合開発環境(IDE)はPycharmを使用していきます。また、DBサーバーはPostgreSQLを使用します。pythonやPycharm、PostgreSQLのインストールについては、ずいぶん前ではありますが、記事を上げているので、そちらを参照してください。
 まず、Pycharmで作業を進めます。

1.プロジェクトの作成

 「新規プロジェクト」をクリックし、プロジェクトを新規作成します。

 












2.プロジェクトの保存先とプロジェクト名をLocationにパスとして入力し、「作成」をクリックします。

 今回は、Cドライブ直下に作成した「python_projects」というフォルダに「work_flow」という名のプロジェクトを作成します。
















3.「ファイル」→「設定」をクリックします。



















4.必要なパッケージをインストールする画面を開きます。
 「プロジェクト:work_flow」→「python インタープリター」をクリックします。
















5.まずは一覧左上の「+」をクリックします。










6.必要なパッケージをインストールします。
 今回は「Django」と「psycopg2」をインストールします。
 ・「Django」のインストール
 検索欄に「django」と入力し、一覧に表示された「Django」を選択してから、「パッケージのインストール」をクックします。



















 ・「psycopg2」のインストール
 検索欄に「psycopg2」と入力し、一覧に表示された「」を選択してから、「パッケージのインストール」をクックします。


















7.インストールが完了したら、右上の「×」をクリックします。






8.必要なパッケージ(「Django」と「psycopg2」)がインストールされているのを確認したら「OK」をクリックします。















9.下にある「pythonコンソール」をクリックします。









10.「import django」と入力し、「Enter」キーを押します。









11.djangoがインポートされたのを確認するため、「django.get_version()」と入力し、バージョンを確認します。バージョン情報が表示されていれば、インポート完了です。









12.下にある「ターミナル」をクリックします。









13.「django-admin startproject mysite .」と入力し、「ENTER」キーを押します。「mysite」というプロジェクトを作成します。










14.左上のツリーの部分に「mysite」が作成されます。










15.開発環境用のWebサーバーと起動します。「python manage.py runserver」と入力し、「ENTER」キーを押します。










16.「http://127.0.0.1:8000/」部分をクリックするか、ブラウザのアドレス入力欄に「http://127.0.0.1:8000/」を入力します。











17.以下の画像のページが表示されれば、プロジェクト作成まで完了です。











18.ターミナルで、「Cntl + C」でサーバーを停止させます。
 赤枠の表示がされれば、開発環境用のWebサーバーは停止状態です。










19.「pyhton manage.py startapp wf」と入力し、「ENTER」キーを押します。「wf」というプロジェクトを作成します。wfはworkflowの略です。




20.左上のツリーの部分に「wf」が作成されます。
















21.「mysite」→「settings.py」をクリックして、settins.pyを編集します。
















22.内容は以下のようにします。
"""
Django settings for mysite project.

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

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

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

import os
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/4.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-m!ac$7q)7(h)ig0wny@v+obe+7s^1pz2l0l)y==@msyyb33^m4'

# 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',
'wf.apps.WfConfig',
]

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 = 'mysite.urls'

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(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 = 'mysite.wsgi.application'


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

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'wf',
'USER': 'django_user',
'PASSWORD': 'password',
'HOST': 'localhost',
'PORT': '',
}
}


# Password validation
# https://docs.djangoproject.com/en/4.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/4.2/topics/i18n/

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

USE_I18N = True

USE_TZ = True


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

STATIC_URL = 'static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

LOGOUT_REDIRECT_URL = '/wf/'

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

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'



以上で、Django側の環境設定が完了です。




 次に、pgAdmin4でpostgreSQLの設定作業を進めます。

最初にpostgreSQLのに「django_user」を作成します。

1.対象のpostgreSQLの「ログイン/グループロール」を右クリックし、「作成」をクリックします。
 ※すでに「django_user」ができているので、今回は「django_user2」の作成で説明します。














2.名前欄に「django_user2」と入力し、「定義」タブをクリックします。

















3.パスワード欄に「password」と入力し、「権限」タブをクリックします。
 ※パスワード欄にはsetteings.pyの「DATABASE」の項目に設定したパスワードを入力します。
















4.すべての項目を「ON」にし、「保存」をクリックします。
















次にpostgreSQLのに「fw」というdbを作成します。
1.「データベース」を右クリックし、「作成」→「データベース」をクリックします。












2.データベース欄に「wf」と入力し、所有者に「django_user」を選択して、「保存」をクックします。
















 以上で、postgreSQL側の環境設定が完了です。

 いよいよ、次回からアプリの作成に入っていきます。