Django:Views.pyでmodels.modelを使い日付毎の集計を行う方法
Django:Views.pyでmodels.modelを使い日付毎の集計を行う方法
本当はpandasによる集計で完結したかったのですが、グラフを書こうとしたら「そうもいかない事情」が発生。
グラフ描写プラグインに【 [UNIX時, 値] 】の配列で渡さないといけない
せかっくPandasで集計したのでコレを生かしたかったのですが、DataFrameにしたものをまたListに戻すと言うどうも納得できない処理が発生したので「だったらSQL側で抽出しよう」との考えに至りました。
構成の概要
■models.py
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 |
from django.db import models from django.core.validators import RegexValidator class Dailies(models.Model): """日計基礎DATA""" observation_date = models.DateField(verbose_name='観測日') client = models.ForeignKey(Clients, verbose_name='クライアント', on_delete=models.PROTECT) shop = models.ForeignKey(Shops, verbose_name='店舗', on_delete=models.PROTECT) TYPE_CHOICES = ( (1, '総合'), (2, 'PC'), (3, 'SP'), (4, 'アプリ'), ) types = models.IntegerField(verbose_name='販売メディア', choices=TYPE_CHOICES) sales = models.FloatField(verbose_name='売上高(税別)') contracts = models.FloatField(verbose_name='売上件数', null=True) access_count = models.FloatField(verbose_name='アクセス数', null=True) conversion_rate = models.FloatField(verbose_name='転換率', null=True) customer_unit_price = models.FloatField(verbose_name='客単価', null=True) average_purchases = models.FloatField(verbose_name='平均購入点数', null=True) items_ordered = models.FloatField(verbose_name='注文点数', null=True) page_views = models.FloatField(verbose_name='ページビュー', null=True) staying_time = models.FloatField(verbose_name='平均滞在時間', null=True) page_migrations = models.FloatField(verbose_name='平均回遊ページ数', null=True) created_at = models.DateTimeField(verbose_name='作成日時', auto_now_add=True) updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True) class Meta: verbose_name_plural = 'Dailies' def __str__(self): return self.observation_date |
■urls.py
1 2 3 4 5 6 7 |
from django.urls import path from . import views app_name = 'repo' urlpatterns = [ path('<int:pk>/', views.DetailView, name="client_show"), ] |
こんな感じでmodelsに登録しました。
repo/<int:pk>でクライアント毎の集計データをグラフで表現し最終的に下のようなグラフに描写します。
Views.pyでmodels.modelクラスを継承してGroup_by & SUMを実行
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 |
import logging, datetime from datetime import date, timedelta from dateutil.relativedelta import relativedelta from django.shortcuts import render from django.views import generic from .models import Dailies from django.views.decorators.csrf import csrf_exempt from django.db.models import Avg, Count, Min, Sum logger = logging.getLogger(__name__) @login_required @csrf_exempt def DetailView(request, pk): (中略) """データ抽出日時調整""" d = datetime.date.today()# 今日 yd = (d - datetime.timedelta(days=1))# 昨日 fd = yd.replace(day=1)# 同月1日 ed = yd.replace(day=calendar.monthrange(d.year, d.month)[1])# 同月末日 """Models.Modelクラスからクライアントの情報を抽出""" d_box = Dailies.objects.filter(observation_date__range=[fd, ed])\ .filter(types=1)\ .filter(client_id=pk) """salesカラムを日付ごとにsum集計"""" list_sales = d_box.values('observation_date').order_by('observation_date')\ .annotate(total_sale=Sum('sales')) (後略) |
30行目~31行目:日毎sum集計の解説
前段の25行目~27行目でクライアントデータと端末(総合)の抽出が終わっています。
30行目では d_box で抽出した値から日付データを選択し昇順並替えを実行しています。
注目点はこの【 オブジェクト名.values(‘集計区分’)】の場所です。
例えば、ここを [ d_box.values(‘observation_date’,’sales’) ] とした場合、observation_dateとsalesの2軸でGroup_byが実行されます。
なので、例えば端末ごとの集計を出したい時などは下のようにすればOKです。
1 |
list_sales = d_box.values('observation_date').order_by('observation_date','types').annotate(total_sale=Sum('sales')) |
31行目はWEB上に誤った情報を多く見かけました。
sumでも間違いと言う事はできません。ただし、select でSQLを直接呼び出すなど今回とは違うルートで合算する場合です。models.modelからのルートの場合は【 Sum 】が正しいです。
unsupported operand type(s) for +: ‘int’ and ‘str’が発生する
models.modelクラスでsumを使うと文字列は集計できないとエラー発生
これは「modelから送られてくるときに数値をテキスト化しているから」なのだそうです。
※この理由については真実性を確かめる事が出来ませんでしたが、sumとした場合のエラー再現性は100%でした。
上記エラーを回避するために利用するのが【from django.db.models import Avg, Count, Min, Sum】です。
ぱっと見でわかる通り、modelsクラスで平均、カウント、集計などを使える様にします。
models.modelクラスを継承しての集計はこのSumを使うので「sumではないよ、Sumなのよ」となります。
まとめ
分析ページ内に複数のグラフを配置したいのですが、グラフ用の配列を作るのが結構大変です。
「きっと上手くやる方法あるんだろうなぁ」と思いながら取り合えず力業でもがいています。
-
前の記事
Bootstrap:daterangepickerで選択しPOSTしてMYSQLの値を抽出する方法 2020.12.15
-
次の記事
Bootstrap:多機能なグラフ描写が可能な『ECharts』の取扱いを調べた 2020.12.17