0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Djangoで検索・ダッシュボード機能を作ってみた。

Last updated at Posted at 2025-02-23

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

いよいよ完成も近くなってきました。今回は、検索機能とダッシュボード機能を作っていきます。

検索機能の作成

今回は、商談履歴(上記の記事で作成)を年月、顧客名、カテゴリで検索できる機能を追加していきたいと思います。

filters.pyの作成

検索機能を追加するのdjango-filterを使用していきます。

pip install django-filter

インストールしてない人は、上記でインストールしよう。
django-filterを使うと検索条件を短いコードで作成することができ、簡単に検索フォームを作ることができます。今回作成したfilters.pymodels.pyとかと同じフォルダcrm_appに保存します。
さっそく作ったfilters.pyを見ていきましょう。

crm_app/filters.py
import django_filters
from .models import Deal

class DealFilter(django_filters.FilterSet):
    year = django_filters.NumberFilter(field_name='date',lookup_expr='year',label='')
    month = django_filters.NumberFilter(field_name='date',lookup_expr='month',label='')
    customer_name = django_filters.CharFilter(field_name='customer',lookup_expr='icontains',label='顧客名')
    category = django_filters.ModelChoiceFilter(
        queryset = Category.objects.all(),
        label = 'カテゴリ',
    )
    
    class Meta:
        model = Deal
        fields = ['year','month','customer_name','category']

まずは、djnago-filterNumberFilterを使用して、数値に対しての検索フィルターを作成しています。Dealクラスのdateフィールドを対象として、年月をフィルタリングしています。引数のlookup_exprについて、後ほど詳しく説明します。

次にCharFilterを使って、文字列に対しての検索フィルターを作成しています。customerフィールドを対象とすることで、顧客名でフィルタリングしています。

最後にModelChoiceFilterを使って、カテゴリで検索ができるようにしています。querysetCategoryモデルのすべてのオブジェクトを取得し、プルダウン形式(選択式)で選択できるフィルターを作成しています。

filters.pyの適用

では、作成したfilters.pyview.pyに適用していきます。

crm_app/views.py
from .filters import DealFilter
...
#商談履歴の閲覧
class DealList(View):
    def get(self,request):
        sort_by = request.GET.get('sort', 'date')  # デフォルトは商談日でソート
        deals = Deal.objects.all().order_by(sort_by)
        
        deal_filter = DealFilter(request.GET,queryset=deals)#フィルタ適用
        filtered_deals = deal_filter.qs
        
        
        paginator = Paginator(filtered_deals,10) #1ページに10件
        page = request.GET.get('page')
        paginated_deals = paginator.get_page(page)
        
        
        context = {
            'deal_filter':deal_filter,
            'deals':paginated_deals,#フィルタ後のデータのみを渡す
        }
        return render(request,'crm_app/deal_list.html',context) 

まずは、sort_by変数を作成して、ソート(並び替え)をしていきます。sort_byはURLパラメータにsortが設定されていたら、それを採用し、なければdateでソートするというものです。

次にDealFilterを使用して、検索フォームに入力された内容に対して、Dealモデル内でフィルタリングを行います。
deal_filter = DealFilter(request.GET,queryset=deals)は、GETリクエストの内容を基にフィルタリングしています。取得したdealsオブジェクト内に対しての検索フォームを表しています。フィルタリングを行ったら、.qsを使用して、フィルタリング後のデータだけ取得します。

ページネーションに渡す値をフィルタリング後のものに変え、検索フォームであるdeal_filterとフィルタリング後の値をテンプレートに渡しています。

テンプレート以前作成したもの検索フォーム部分を追加します。

templates/crm_app/deal_list.html
...
    <form method="GET">
        {{ deal_filter.form.as_p }}
        <button type="submit">検索</button>
    </form>
...

これで、検索機能の完成です。

補足:lookup_exprについて

lookup_exprとはdjango-filterで検索するとき、どんな条件で検索するかを決めるオプションです。以下が主に使われるものです。

lookup_expr 意味
exact 完全一致
iexact 大文字小文字を無視した一致
contains 部分一致
icontains 大文字小文字を無視した部分一致
year 年での絞り込み
month 月での絞り込み
day 日での絞り込み
gt より大きい
gte 以上
lt より小さい
lte 以下

ダッシュボードの作成

作業内容

  1. 基本的なダッシュボード作成
    • 顧客数、商談数、売上予測(ダミーデータ可)を表示
  2. データ集計
    • KPIを計算してテンプレートに表示
  3. グラフ表示
    • chart.js などを使用して簡単なグラフを表示

ダッシュボードのビュー作成

まずは、ビューを作成していきます。ダッシュボートで必要なのは、顧客数、商談数、売上予測(今回はダミーデータを使用)です。それらを用意していきます。

crm_app/views.py
class Dashboard(View):
    def get(self,request):
        #基本データの取得
        customer_count = Customer.objects.count() #顧客数
        deal_count = Deal.objects.count() #商談数
        
        #売上予測(ダミーデータ)
        sales_forecast = sum(random.randint(10000,50000) for _ in range(deal_count)) #仮の売上予測
        
        #月ごとの商談件数(グラフ用データ)
        monthly_deals = (
            Deal.objects
            .extra(select={'month':"strftime('%Y-%m',date)"}) #SQLite用(PostgreSQLなら'date_trunc'
            .values('month')
            .annotate(count=Count('id'))
            .order_by('month')
        )
        
        months = [entry['month'] for entry in monthly_deals]  # x軸(月)
        deal_counts = [entry['count'] for entry in monthly_deals]  # y軸(商談数)
        
        
        context = {
            'customer_count':customer_count,
            'deal_count':deal_count,
            'sales_forecast':sales_forecast,
            'months':months,
            'deal_counts':deal_counts,
        }
        
        return render(request,'crm_app/dashboard.html',context)

まずは、CustomerクラスとDealクラスから顧客数と商談数を取得します。数を数えるのは、count()を使用しています。次に売上予測のダミーデータを作成しているのですが、こちらは1商談で10000~50000円として、ランダムな数値を割り振っています。

次にmonthly_dealsについてです。こちらは月ごとの商談件数のデータをグラフ用に作成している部分です。
上からそれぞれ、

  • Dealモデルを指定
  • dateフォールドの日付をYYYY-MMの形に変形し、monthという名前の列を作成
  • monthごとにデータをグループ化
  • 各月ごとの商談数を```id``をカウントすることで取得
  • 月ごとに並び替え
    を行っています。

次に、monthsdeal_countsはx軸とy軸のデータを作成しています。monthly_dealsからx軸には月ごとのデータを、y軸には商談数のデータを格納している。

最後に、作成したデータをテンプレートに渡しています。

テンプレートの作成

今回は、base.htmlを使用せずに作成しました。base.htmlが使えないというわけではなく、新しくjavascriptファイルを使用する関係で、見直すときにわかりやすいかなという理由です。関係ないよって人はぜひbase.htmlを編集してみてください。

templates/crm_app/dashboard.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ダッシュボード</title>
    <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/chart.js"></script>
</head>
<body>
    {% load humanize %}
    <div class="container mt-5">
        <h2>ダッシュボード</h2>

        <!-- ✅ KPI セクション -->
        <div class="row text-center">
            <div class="col-md-4">
                <div class="card text-white bg-primary mb-3">
                    <div class="card-header">顧客数</div>
                    <div class="card-body">
                        <h3>{{ customer_count }}</h3>
                    </div>
                </div>
            </div>
            <div class="col-md-4">
                <div class="card text-white bg-success mb-3">
                    <div class="card-header">商談数</div>
                    <div class="card-body">
                        <h3>{{ deal_count }}</h3>
                    </div>
                </div>
            </div>
            <div class="col-md-4">
                <div class="card text-white bg-warning mb-3">
                    <div class="card-header">売上予測</div>
                    <div class="card-body">
                        <h3>¥{{ sales_forecast|intcomma }}</h3>
                    </div>
                </div>
            </div>
        </div>

        <!-- ✅ 商談数の推移グラフ -->
        <div class="card mt-4">
            <div class="card-header">月ごとの商談件数</div>
            <div class="card-body">
                <canvas id="dealChart"></canvas>
            </div>
        </div>
    </div>

    <script>
        var ctx = document.getElementById('dealChart').getContext('2d');
        var dealChart = new Chart(ctx, {
            type: 'bar',
            data: {
                labels: {{ months|safe }},  // x軸
                datasets: [{
                    label: '商談件数',
                    data: {{ deal_counts|safe }},  // y軸
                    backgroundColor: 'rgba(54, 162, 235, 0.6)',
                    borderColor: 'rgba(54, 162, 235, 1)',
                    borderWidth: 1
                }]
            },
            options: {
                scales: {
                    y: { beginAtZero: true }
                }
            }
        });
    </script>
</body>
</html>

まずグラフ表示のためにchart.jsを使用します。ヘッダーのところにscriptで設定します。
まずはKPIセクションについてです。KPIとは「ビジネスの成果を測るための重要な指標」のことで、要するに成果として、この項目を見ようねっていうことです。今回は、「顧客数」「商談数」「売上予測」の3つの指標を表示しています。

次にグラフを表示している部分についてです。グラフは<canvas id="dealChart"></canvas>の部分に表示されます。設定しているのが、下の<script></script>部分です。

script部分は、
var ctx = document.getElementById(’dealChart’).getContext(’2d’):id=”dealChart”の要素を取得し、2Dグラフィックスを描写するためのコンテキストを取得。html内にの記述が必要。

var dealChart = new Chart(): Chartコンストラクタを使用してグラフを生成。引数のtypeはグラフの種類を設定。今回使用したbarは棒グラフ。他にもline:折れ線グラフやpie:円グラフなどがある。

引数のdata{}:グラフ表示するデータの設定を行う。引数のlabels:{{ months|safe }}は棒グラフのx軸ラベル。months変数が表示される。

引数のdatasets:実際のグラフデータを定義する配列。複数のデータセットを追加することで、複数の系列で表示できる。borderWidthは棒グラフの太さを指定する。

options:{}:グラフ表示のオプションを設定。scalesは軸の設定。今回はy軸を設定している。beginAtZero:trueは目盛りが0から始めるように設定している。

となっています。
完成したページがこちらです。
crmシステム_dashboard.jpg

一件しか追加してないですが、しっかりと表示できていますね。
urls.pyの部分は割愛してますが、今までと同じように設定するだけです。

これで、顧客管理システム完成です。まだフォームとかの見た目気を使ってない部分多いですので、改良してみると楽しいかもしれません。

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

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?