顧客管理システムのCRUD機能の実装

この記事は、「Djangoを使用した顧客管理システムの作成」の3つ目の記事です。

https://qiita.com/tomo0227/items/cc893ea4e8e6cfb6ad77

## CRUD機能の実装
### 作業内容

https://qiita.com/tomo0227/items/11f6a262ee1da183fd70

上記の記事で作成したモデルを使って以下の作業を行っていきます。
1. ビューとテンプレートの作成
    * 顧客の登録・閲覧・編集・削除機能(CRUD)
2. フォームの作成
    * Djangoのフォームを活用し、入力画面を実装
3. ルーティング
    * URLパターンを設定し、CRUD機能に対応
4. 簡単なUIの作成
    * Bootstrapを使用してフォームやリストをスタイリング
### ビューの作成
``` crm_app ```フォルダの中の``` views.py ```を編集してビューの作成を行います。ビューの作成にはDjangoの基底クラス``` View ```を使用します。
以下が作成したビューになります。
``` :crm_app/views.py
from django.shortcuts import render,redirect,get_object_or_404
from django.views import View
from .models import Customer,Category,Deal
from .forms import AddCustomerForm

#顧客情報のリスト閲覧
class CustomerList(View):
    def get(self,request):
        customer = Customer.objects.all()
        return render(request,'crm_app/customer_list.html',{'customer':customer}) 

#顧客情報の追加
class AddCustomer(View):
    def get(self,request):
        form = AddCustomerForm()
        return render(request,'crm_app/add_customer.html',{'form':form})

    def post(self,request):
        form = AddCustomerForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('customer_list')
        return render(request,'crm_app/add_customer.html',{'form':form})


#顧客情報の編集
class EditCustomer(View):
    def get(self,request,id):
        customer = get_object_or_404(Customer,id=id)
        form = AddCustomerForm(instance=customer)
        return render(request,'crm_app/edit_customer.html',{'form':form,'id':id})
        
    
    def post(self,request,id):
        customer = get_object_or_404(Customer,id=id)
        form = AddCustomerForm(request.POST,instance=customer)
        if form.is_valid():
            form.save()
            return redirect('customer_list')
        return render(request,'crm_app/edit_customer.html',{'form':form,'id':id})

#顧客情報の削除
class DeleteCustomer(View):
    def get(self,request,id):
        customer = get_object_or_404(Customer,id=id)
        return render(request,'crm_app/delete_customer.html',{'customer':customer})
    
    def post(self,request,id):
        customer = get_object_or_404(Customer,id=id)
        customer.delete()
        return redirect('customer_list')

customer_list = CustomerList.as_view()
add_customer = AddCustomer.as_view()
edit_customer = EditCustomer.as_view()
delete_customer = DeleteCustomer.as_view()
```
機能としては、
``` CustomterList ```が顧客情報の一覧を表示するクラスです。``` Customer ```モデルの全データを取得して、テンプレート``` customer_list.html ```に渡しています。

``` AddCustomer ```は顧客情報を追加するクラスです。``` get ```で空のフォームを表示し、``` post ```で入力されたフォームのデータを保持し、``` customer_list ```にリダイレクトします。```AddCutomerForm```の作成については後ほど記述する。

``` EditCustomer ```は顧客情報を編集するクラスです。``` get ```で指定した``` id ```の顧客データを取得し、フォームに表示、``` post ```でフォームのデータを更新し、``` customer_list ```にリダイレクトします。

``` DeleteCutomer ```は顧客情報を削除するクラスです。``` get ```で削除確認ページを表示し、``` post ```で指定した``` id ```の顧客を削除し、``` customer_list ```にリダイレクトします。

ポイントとして、``` get_object_or_404() ```を使用し、存在しない``` id ```が指定されたらエラーページを返すようにしています。
### テンプレートの作成
作成したビューを基に``` customer_list.html  ```,``` add_customer.html ```,``` edit_customer.html ```,``` delete_customer.html ```を作成していきます。
まずはベースとなるテンプレート``` base.html ```を作成します。これを作成すると、各テンプレートで同じ記述をする部分を省略できるようになります。
テンプレートファイルの保存場所は、``` crm_app ```フォルダの中に``` templates/crm_app ```を作成し、保存します。ただし、``` base.html ```のみ``` crm_app/templates/ ```に保存する。
```:crm_app/templates/base.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CRM System</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-brand" href="{% url 'customer_list' %}">CRM System</a>
        </div>
    </nav>

    {% block content %}
    {% endblock %}

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
```
この後、簡単なUIを作成するのにBootstrapを使用するので、インポートしておきます。```base.html```を作成することで、他のテンプレートファイルは```{% block content %}```,```{% endblock %}```間のみの記述するだけで済みます。

次に顧客情報の一覧表示をする```customer_list.html```を作成していきます。
``` :templates/crm_app/customer_list.html
{% extends 'base.html' %}

{% block content %}
<div class="container mt-4">
    <h2>顧客一覧</h2>
    <a href="{% url 'add_customer' %}" class="btn btn-primary mb-3">新規顧客登録</a>
    
    <table class="table table-striped">
        <thead>
            <tr>
                <th>ID</th>
                <th>名前</th>
                <th>メール</th>
                <th>カテゴリー</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            {% for c in customer %}
            <tr>
                <td>{{ c.id }}</td>
                <td>{{ c.name }}</td>
                <td>{{ c.email }}</td>
                <td>{{ c.category.name }}</td>
                <td>
                    <a href="{% url 'edit_customer' c.id %}" class="btn btn-sm btn-warning">編集</a>
                    <a href="{% url 'delete_customer' c.id %}" class="btn btn-sm btn-danger">削除</a>
                </td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
</div>
{% endblock %}
```
```{% extends 'base.html' %}```の記述で先ほど作成した```base.html```を反映しています。```CustomerList```クラスの戻り値として渡された```customer```を```for```文を利用して、処理しています。

次に顧客情報を追加する```add_customer.html```を作成していきます。
```:templates/crm_app/add_customer.html
{% extends 'base.html' %}

{% block content %}
<div class="container mt-4">
    <h2>顧客登録</h2>
    <form method="post">
        {% csrf_token %}
        <div class="form-group">
            {{ form.as_p }}
        </div>
        <button type="submit" class="btn btn-primary">登録</button>
        <a href="{% url 'customer_list' %}" class="btn btn-secondary">戻る</a>
    </form>
</div>
{% endblock %}
```
```add_customer.html```では```AddCustomer```クラスの戻り値であるフォーム```form```が使われている。フォームを使うときは、```{% csrf_token %}```を記述します。これは、CSRF(Cross-Site Request Forgery)対策に使われるトークンで、フォーム送信時のセキュリティ対策、ユーザーの意図しないリクエストを防ぐための認証トークンなどの役割があります。

次に顧客情報の編集を行う```edit_customer.html```を作成していきます。
```:templates/crm_app/edit_customer.html
{% extends 'base.html' %}

{% block content %}
<div class="container mt-4">
    <h2>顧客情報編集</h2>
    <form method="post">
        {% csrf_token %}
        <div class="form-group">
            {{ form.as_p }}
        </div>
        <button type="submit" class="btn btn-primary">更新</button>
        <a href="{% url 'customer_list' %}" class="btn btn-secondary">戻る</a>
    </form>
</div>
{% endblock %}
```
```edit_customer.html```は顧客情報を追加するときと見た目はほぼ一緒なので、```add_customer.html```とほぼ同じ内容になっています。

次に```delete_customer.html```を作成していきます。
```:templates/crm_app/delete_customer.html
{% extends 'base.html' %}

{% block content %}
<div class="container mt-4">
    <h2>顧客削除確認</h2>
    <p>以下の顧客情報を削除してもよろしいですか？</p>
    
    <div class="card mb-3">
        <div class="card-body">
            <h5 class="card-title">{{ customer.name }}</h5>
            <p class="card-text">メール: {{ customer.email }}</p>
            <p class="card-text">カテゴリー: {{ customer.category.name }}</p>
        </div>
    </div>

    <form method="post">
        {% csrf_token %}
        <button type="submit" class="btn btn-danger">削除</button>
        <a href="{% url 'customer_list' %}" class="btn btn-secondary">戻る</a>
    </form>
</div>
{% endblock %}
```
削除項目がわかりやすいように```DeleteCustomer```クラスの戻り値として渡された```customer```から各要素を表示されるようにしました。
### フォームの作成
```views.py```で記述している```AddCustomerForm```を作成していきます。
```crm_app```フォルダの中に```forms.py```を作成し、Djangoの```forms```を```import```してフォームの作成を行っていきます。(注：```templates/crm_app```フォルダの中ではない)
```:crm_app/forms.py
from django import forms
from .models import Customer

class AddCustomerForm(forms.ModelForm):
    class Meta:
        model = Customer
        fields = ['name','email','phone','address','categories']
```
使用するモデルと入力項目を記述するだけで、フォームを作成することができます。
### ルーティング設定
実際にサーバーをたてて、web上に表示させるときのURLパターンの設定を行っていきます。ルーティング設定は、プロジェクトとアプリケーションの両方で行う必要があります。

まずはプロジェクトのルーティング設定から行っていきます。
```:crm_project/urls.py
from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('crm_app.urls')), #追加
]
```
このファイルは、プロジェクト作成時に自動的に作成されるもので、作成したアプリケーションに合わせて設定を行います。

次にアプリケーションのルーティング設定を行っていきます。アプリケーションのルーティング設定では、自動的に```urls.py```が作成されないので、```crm_app/urls.py```を作成し、設定を行います。
```:crm_app/urls.py
from django.urls import path
from .views import customer_list,add_customer,edit_customer,delete_customer

app_name = 'crm_app'
urlpatterns = [
    path('customers/', customer_list, name='customer_list'),
    path('customers/add/', add_customer, name='add_customer'),
    path('customer/<id>/edit/',edit_customer,name='edit_customer'),
    path('customer/<id>/delete/',delete_customer,name='delete_customer'),
]
```
上記のように任意のURLパターンを作成することができます。
### 簡単なUIの作成
今回はBootstrapを使用して、UIの作成を行っていきます。
#### Bootstrapとは
BootstrapとはWebサイトやWebアプリのデザインを簡単に整えるためのCSSフレームワークです。特徴として、レスポンシブ対応している、CSSの知識が少なくてもデザインを整えられる、組み込みのデザイン(ボタン・フォーム・ナビバーなど)が豊富などがあげられます。
#### Bootstrapのインポート
```templates/base.html```に記述されている
```<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">```
と
```<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>```
が、BootstrapのCSSとJavaScriptをインポートしている部分です。この記述によってBootstrapのすべてのスタイルが使用可能になります。
使用方法は、指定の```class```を設定することによって簡単にデザインを整えてくれます。めちゃくちゃ便利です。

今回はこれで以上です。次回は商談履歴の管理機能を追加していきます。

1つ目：Djangoを使用した顧客管理システムの作成
https://qiita.com/tomo0227/items/cc893ea4e8e6cfb6ad77
2つ目：顧客管理システムのモデル作成
https://qiita.com/tomo0227/items/11f6a262ee1da183fd70
3つ目：顧客管理システムのCRUD機能の実装
https://qiita.com/tomo0227/items/054c974e104f81ea82db
4つ目：商談履歴管理システムの作成
https://qiita.com/tomo0227/items/7b9934ab4bc36cdcd4e8
5つ目：Djangoで検索・ダッシュボード機能を作ってみた。
https://qiita.com/tomo0227/items/a86f7bf038e87be11892