2024-12-11

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

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

1.基本となる画面作成

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

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






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




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

<!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を以下のように変更します。

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


<!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を継承するように、以下のように変更する。


{% 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に対応するビューを作成します。

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経由で行うビューの例です。

# タスクの追加
@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で非同期に行う場合の例です。

# タスクの削除
@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でフォームを定義します。

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でルーティングを設定します。

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を含めることで、動的にタスクを操作できます。

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でのワークフロー作成・編集機能が実装できます。

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