djangoでレコードの集計してたら、どうしてもできない集計があることに気づいた。
仕方がないので、直接sql発行して集計させることにした。
sqlを文字列として準備し発行すると、仮にexceptionが発生したときにもそれをエラーログとして出力させるようにした。
ログ出力はmariadbに出力するようにしてる。
今回はレコード集計結果を作ったのでそのメモ。
結論
djangoで経費のレコードを処理する画面を作ってる途中。
これから「編集」と「操作」の列にボタン足して編集・コピー・削除の機能をつけてこうとしてる。
他の画面で既に「編集」と「操作」は作ってあるから、djangoのviewsにはコピーして作れば余裕。
経費テーブルには、かかった費用の目安を記録しときたいから、自作の科目も含んでて、実際の「水道代」とか「外食費」もつけて台帳管理してる。
自宅で業務をしてると「水道光熱費」ってのは、実生活でかかる水道代や電気代を業務と生活で按分しなきゃいけないから、全部を計上しちゃいけない。
確定申告で使う経費科目は決まっているから、自作の経費科目は経費計上しないようにしたい。
けれど、djangoではその科目に応じた集計をさせられない場面があった。
予想してたけど、やっぱりできん集計あるんやなぁ。
(自分がまだ見つけられてないだけかもしれん)
さっさと進めて作りたかったから、直接SQLの文字列を発行して集計結果を表示させるようにした。
djangoの基本機能で集計できなかったのは、経費合計。
理由は経費テーブルから外部参照する科目テーブルに、経費対象の列があって、外部参照する列をdjangoのfilterで指定できなかったから。
指定するとエラーになって、実テーブルに存在する項目しか指定を許可してくれなかったから。
その他の、医療費控除とか売上合計なんかの科目狙い打ち集計はできてた。
自分のテーブル設計がマズいのかもしれんけど、既存のphp版からの移行作成やから、保管構造は変えない方針。
(idの列は足したけど・・・)
処理優先で考えるんじゃなく、データベース優先で考えるから、djangoの機能を使えるところだけ使って、実際の集計はsql書くことにした。
既存のphp版にある過去7年分、84か月分の経費合計がきっちりあってた。1円も狂ってなくて気持ちよかったなぁ。
djangoは「SQL書かなくても動く」ってのがいいところ、みたいな書き方を見かけるけど、DBMSの集計機能を使ってSQLで書いたほうがいいこともあると思う。
実際の開発でSQL書けない人がたまにいるけど、RDBを扱うときにSQL書けなかったら、本番開始後のDB関連障害や高速化に対応できないんじゃないかとも思う。
例えばoracleからpostgresqlに移行するときのことを考えて、sqlは一切書かずに全部djangoに任せるっていうのはいいことかもしれんけど、そういう見込みがないんやったらベタのSQL書いて楽したほうがええんとちゃうか?
サンプルデータ
例えばこんなデータがあったとする。
月間の売上とか経費が入ってる。
経費テーブル
workPeriodは、経費計上の年月だけ使ってるから、全部1日の0:00:00って入ってる。
Kamokuは、科目テーブルにある科目番号と科目名が埋め込んである。
id | workPeriod | workShubetsu | workPriority | Keihi_date | Kamoku | Tehai | Kng | Biko |
---|---|---|---|---|---|---|---|---|
9058 | 2022/03/01/火 0:00:00 | 1yan | 1001 | 2022/03/10/木 0:00:00 | 008-給料賃金 | 給与 | 60000 | 20140924申請の青色事業者専従者給与に関する届出書により10万円を目標に計上。 |
9059 | 2022/03/01/火 0:00:00 | 1yan | 1002 | 2022/03/10/木 0:00:00 | 020-水道光熱費 | 光熱費 | 10000 | 50%を事業用の電気代とする。月平均2万円x50%=1万円。 |
9068 | 2022/03/01/火 0:00:00 | 1yan | 1003 | 2022/03/10/木 0:00:00 | 025-通信費 | インターネット/電話利用料 | 10000 | 電話とインターネットは50%を事業用の電気代とする。月平均2万円x50%=1万円。 |
9060 | 2022/03/01/火 0:00:00 | 2kgc | 1010 | 2022/03/31/木 0:00:00 | 101-現金売上 | amazon販売 | 0 | 物販(3/xx) |
9077 | 2022/03/01/火 0:00:00 | 2kgc | 1020 | 2022/03/10/木 0:00:00 | 102-預金売上 | お客様 | 100000 | xxx案件 |
9074 | 2022/03/01/火 0:00:00 | 2kgc | 1047 | 2022/03/31/木 0:00:00 | 023-損害保険料 | xx保険 | 6550 | エージェント会社提供の掛け捨て保険 |
9087 | 2022/03/01/火 0:00:00 | 2kgc | 1068 | 2022/03/05/土 0:00:00 | 003-研究開発費 | 定食屋 | 1630 | IT市場調査。昼食。 |
9070 | 2022/03/01/火 0:00:00 | 2kgc | 1072 | 2022/03/06/日 0:00:00 | 001-打合会議費 | 喫茶xx | 2480 | 月初打合せ |
9101 | 2022/03/01/火 0:00:00 | 2kgc | 2019 | 2022/03/20/日 0:00:00 | 003-研究開発費 | apple | 4787 | mp4解析用 |
9055 | 2022/03/01/火 0:00:00 | 3ikk | 1042 | 2022/03/31/木 0:00:00 | 038-外食費 | 外食費累積 | 1467 | 目安 |
9056 | 2022/03/01/火 0:00:00 | 3ikk | 1043 | 2022/03/31/木 0:00:00 | 039-食費 | 食費累積 | 74120 | 目安 |
9051 | 2022/03/01/火 0:00:00 | 3ikk | 1045 | 2022/03/31/木 0:00:00 | 099-預金残高 | 銀行残高 | 0 | 残高確定 |
9085 | 2022/03/01/火 0:00:00 | 3ikk | 1046 | 2022/03/31/木 0:00:00 | 040-水道代 | 大阪市水道局 | 3842 | 1月分水道代。1月13日検針。22立米。(クレカ引き落とし) |
9054 | 2022/03/01/火 0:00:00 | 3ikk | 1053 | 2022/03/31/木 0:00:00 | 041-電気代 | 関西電力 | 19738 | 3月分電気代。919kwh。 |
経費種別テーブル
workShubetsuは経費の種別で、djangoのtemplatesで表示させるときの色指定が一緒に入ってる。
id | keihiNo | keihiName | keihiSetsumei | foColor | bgColor |
---|---|---|---|---|---|
1 | 1 | 1yan | 家賃・固定費 | #ffffff | #E9967A |
2 | 2 | 2kgc | 小口現金 | #ffffff | #8FBC8F |
3 | 3 | 3ikk | 一括支払 | #ffffff | #00CED1 |
科目テーブル
科目テーブルは一般書籍を見てかなり昔に作った。
自分にとってはほぼ普遍的なデータ。
kamokuNoは自分用に勝手に作ったもの。
kamokuNameとペアで経費テーブルに入れるとき使う。
kamokuNoが100番台は自作の科目。90番台は当月の現金と預金の残高を強制セットするために作ってあって、お釣りもらい忘れなんかで実際の残高とあわないときに使う。
hyojiOrderはdjangoのtemplatesでリストボックス表示するときの表示順が入ってる。
keihiTaishoは経費として計上する対象を1にして設定してある。
id | kamokuNo | kamokuName | hyojiOrder | keihiTaisho |
---|---|---|---|---|
2 | 2 | 前年繰越-元入金 | 1 | 0 |
46 | 101 | 現金売上 | 2 | 0 |
47 | 102 | 預金売上 | 3 | 0 |
45 | 100 | 活動費 | 201 | 0 |
32 | 32 | 現金移動 | 202 | 0 |
48 | 103 | 現金仕入 | 301 | 1 |
49 | 104 | 預金仕入 | 302 | 1 |
4 | 4 | 医療費控除 | 1003 | 0 |
33 | 33 | 医療費控除(貯金切り崩し) | 1004 | 0 |
1 | 1 | 打合会議費 | 1005 | 1 |
3 | 3 | 研究開発費 | 1006 | 1 |
5 | 5 | 売上原価 | 1007 | 1 |
6 | 6 | 外注工賃 | 1008 | 1 |
7 | 7 | 貸し倒れ金 | 1009 | 1 |
8 | 8 | 給料賃金 | 1010 | 1 |
9 | 9 | 減価償却費 | 1011 | 1 |
10 | 10 | 研修費 | 1012 | 1 |
11 | 11 | 広告宣伝費 | 1013 | 1 |
12 | 12 | 雑費 | 1014 | 1 |
13 | 13 | 支払手数料 | 1015 | 1 |
14 | 14 | 支払報酬 | 1016 | 1 |
15 | 15 | 車両関係費 | 1017 | 1 |
16 | 16 | 修繕費 | 1018 | 1 |
17 | 17 | 消耗品費 | 1019 | 1 |
31 | 31 | IT機材費 | 1019 | 1 |
18 | 18 | 諸会費 | 1020 | 1 |
19 | 19 | 新聞図書費 | 1021 | 1 |
20 | 20 | 水道光熱費 | 1022 | 1 |
21 | 21 | 接待交際費 | 1023 | 1 |
22 | 22 | 租税公課 | 1024 | 1 |
23 | 23 | 損害保険料 | 1025 | 1 |
24 | 24 | 地代家賃 | 1026 | 1 |
25 | 25 | 通信費 | 1027 | 1 |
26 | 26 | 荷造運賃 | 1028 | 1 |
27 | 27 | 福利厚生費 | 1029 | 1 |
28 | 28 | リース料 | 1030 | 1 |
29 | 29 | 利子割引料 | 1031 | 1 |
30 | 30 | 旅費交通費 | 1032 | 1 |
34 | 34 | 国民健康保険 | 8001 | 0 |
35 | 35 | 市税 | 8002 | 0 |
36 | 36 | 固定資産税 | 8003 | 0 |
37 | 37 | 国民年金 | 8004 | 0 |
38 | 38 | 外食費 | 8005 | 0 |
39 | 39 | 食費 | 8006 | 0 |
40 | 40 | 水道代 | 8007 | 0 |
41 | 41 | 電気代 | 8008 | 0 |
42 | 42 | 電話代 | 8009 | 0 |
43 | 98 | 現金残高 | 9001 | 0 |
44 | 99 | 預金残高 | 9002 | 0 |
自作の科目として、たとえば「食費」は経費に計上しちゃいけないけど、「IT機材費」は業務で使うハードウェア・ソフトウェアなので確定申告で科目作って計上してる。
djangoのmodels定義
3つのテーブルはだいたいこんな感じ。
経費テーブルのmodels定義
サンプルデータには入れなかったけど、管理項目として実際には作成・更新のユーザと日付を定義してる。
実テーブルにある項目の定義の後に、“get_“`って定義をして他のテーブルを参照させたり、templates表示用の項目を定義してる。
さらに、後ろのほうに、get_keihitaisho
って書いて、科目テーブルを参照させてるんやけど、この値を条件にしてfilterできんかった。
from django.db import models
from django.utils import timezone
from pymysql import NULL
from datetime import datetime,timedelta ## テーブルの日付フィールドを扱うため
## マスターテーブル使うため
from gvisWebApp.models.GvisMaster import GvisMstKeihishubetsu,GvisMstKamoku
## マスターテーブル絞り込みのため
from django.db.models import Q
class GvisKeihi(models.Model):
## 参考URL https://note.nkmk.me/python-datetime-now-today/
dt_year = datetime.now().year
dt_mon = datetime.now().month
dt_day = datetime.now().day
dt_defdate = str(dt_year) + '-' + str(dt_mon) + '-' + str(dt_day) + ' ' + '00:00:00'
workshubetsu_choicesDB=[(i.keihiname, i.keihisetsumei ) for i in GvisMstKeihishubetsu.objects.all().order_by('keihino')]
kamoku_choicesDB=[
("%03d" % i.kamokuno + "-" + i.kamokuname ,
"%03d" % i.kamokuno + "-" + i.kamokuname) for i in GvisMstKamoku.objects.all().order_by('hyojiorder')]
workperiod = models.DateTimeField(verbose_name='業務年月',db_column='workPeriod', blank=True, null=True,
default=datetime.strptime(dt_defdate,'%Y-%m-%d %H:%M:%S')) # Field name made lowercase.
def __str__(self):
return self.workperiod.strftime('%c')
workshubetsu = models.CharField(verbose_name='経費種別',db_column='workShubetsu', max_length=4,
choices=workshubetsu_choicesDB ,blank=False, null=False) # Field name made lowercase.
def __str__(self):
return self.workshubetsu
workpriority = models.IntegerField(verbose_name='経費No-優先順位',db_column='workPriority', blank=True, null=True) # Field name made lowercase.
def __str__(self):
return self.workpriority
keihi_date = models.DateTimeField(verbose_name='経費日付',db_column='Keihi_date', blank=True, null=True,
default=datetime.strptime(dt_defdate,'%Y-%m-%d %H:%M:%S')) # Field name made lowercase.
def __str__(self):
return self.keihi_date.strftime('%c')
kamoku = models.CharField(verbose_name='科目',db_column='Kamoku', max_length=24,
choices=kamoku_choicesDB ,blank=False, null=False) # Field name made lowercase.
def __str__(self):
return self.kamoku
tehai = models.CharField(verbose_name='手配方法・名称',db_column='Tehai', max_length=1000, blank=True, null=True) # Field name made lowercase.
def __str__(self):
return self.tehai
kng = models.DecimalField(verbose_name='金額',db_column='Kng', max_digits=12, decimal_places=4, blank=True, null=True) # Field name made lowercase.
def __str__(self):
return self.kng
biko = models.CharField(verbose_name='備考',db_column='Biko', max_length=100, blank=True, null=True) # Field name made lowercase.
def __str__(self):
return self.biko
ins_date = models.DateTimeField(blank=True, null=True,
default=datetime.strptime(dt_defdate,'%Y-%m-%d %H:%M:%S')
)
def __str__(self):
return self.ins_date.strftime('%c')
ins_user = models.CharField(max_length=100, blank=True, null=True)
def __str__(self):
return self.ins_user
upd_date = models.DateTimeField(blank=True, null=True,
default=timezone.datetime.min
)
def __str__(self):
return self.upd_date.strftime('%c')
upd_user = models.CharField(max_length=100, blank=True, null=True)
def __str__(self):
return self.upd_user
def get_yyyymm(self):
return self.workperiod.strftime('%Y/%m')
def get_keihi_date(self):
return self.keihi_date.strftime('%d(%a)')
def get_keihi_date_dayofweek(self):
return self.keihi_date.strftime('%a')
def get_workshubetsu(self):
findkey = self.workshubetsu
keihisetsumei=[(i.keihisetsumei) for i in GvisMstKeihishubetsu.objects.filter(Q(keihiname=findkey))]
return keihisetsumei[0]
### 定義してtemplatesには表示こそできても、もviewsの中でfilterには使えず・・・。
def get_keihitaisho(self):
findkey = self.kamoku[4:]
keihitaisho=[(i.keihitaisho) for i in GvisMstKamoku.objects.filter(Q(kamokuname=findkey))]
return keihitaisho[0]
class Meta:
managed = False
db_table = 'GVIS_keihi'
経費種別テーブルのmodels定義
普通に定義。
class GvisMstKeihishubetsu(models.Model):
keihino = models.IntegerField(db_column='keihiNo') # Field name made lowercase.
def __str__(self):
return self.keihino
keihiname = models.CharField(db_column='keihiName', max_length=20, blank=True, null=True) # Field name made lowercase.
def __str__(self):
return self.keihiname
keihisetsumei = models.CharField(db_column='keihiSetsumei', max_length=200, blank=True, null=True) # Field name made lowercase.
def __str__(self):
return self.keihisetsumei
focolor = models.CharField(db_column='foColor', max_length=7, blank=True, null=True) # Field name made lowercase.
def __str__(self):
return self.focolor
bgcolor = models.CharField(db_column='bgColor', max_length=7, blank=True, null=True) # Field name made lowercase.
def __str__(self):
return self.bgcolor
class Meta:
managed = False
db_table = 'GVIS_mst_keihishubetsu'
科目テーブルのmodels定義
こっちも普通に定義。
class GvisMstKamoku(models.Model):
kamokuno = models.IntegerField(db_column='kamokuNo') # Field name made lowercase.
def __str__(self):
return str(self.kamokuno)
kamokuname = models.CharField(db_column='kamokuName', max_length=20, blank=True, null=True) # Field name made lowercase.
def __str__(self):
return self.kamokuname
kamokusetsumei = models.CharField(db_column='kamokuSetsumei', max_length=200, blank=True, null=True) # Field name made lowercase.
def __str__(self):
return self.kamokusetsumei
hyojiorder = models.IntegerField(db_column='hyojiOrder', blank=True, null=True) # Field name made lowercase.
def __str__(self):
return str(self.hyojiorder)
keihitaisho = models.IntegerField(db_column='keihiTaisho', blank=True, null=True) # Field name made lowercase.
def __str__(self):
return str(self.keihitaisho)
class Meta:
managed = False
db_table = 'GVIS_mst_kamoku'
djangoのviewsで計算してる箇所
経費合計の取得がどうしてもうまく行かなかった。
KingakuGokei = object_list.filter(Q(get_keihitaisho=1)).aggregate(sum=Sum('kng'))['sum']
仕方がないので、年月を受け取って経費を取得する関数を作った。self.request
がついてるのは、ログイン情報を渡してログをDBに出力させるため。
KingakuGokei = gv_Func_GetKeihi_ofMonth(workperiodYYYY,workperiodMM,self.request)
以下、viewsの全部。
from django.contrib.auth.decorators import login_required
from django.http import request
from django.utils import timezone
## 関数でログイン必須にするため
from ..models import GvisKeihi ## DB読み込みのため
from django.views.generic import (
ListView, ## リスト表示のため
DetailView, ## 単一レコードの詳細情報表示のため
CreateView, ## レコードの作成のため
DeleteView, ## レコードの削除のため
UpdateView, ## レコードの更新のため
)
from django.db.models import ( Q, ## 検索のため
Max, ## 最大値取得のため
Sum, ## 合計表示させるため
)
from django.contrib.auth.mixins import LoginRequiredMixin
## クラスビューをログイン必須にするため
## 参考URL https://e-tec-memo.herokuapp.com/article/62/
## https://stackoverflow.com/questions/10275164/django-generic-views-using-decorator-login-required
from django.urls import reverse,reverse_lazy
## urlを返すreverse/reverse_lazyを使うため
from ..forms import GvisKeihiForm ## 検索フォームを使うため
from django.contrib import messages ## 通知メッセージを表示するため
from datetime import datetime ## テーブルの日付フィールドを扱うため
from ..common.common import * ## 共通関数
from django.shortcuts import redirect ## ページ表示のため
class GvisKeihiList(LoginRequiredMixin, ListView):
model = GvisKeihi
template_name = 'gvisWebApp/gvis_500_KeihiKensaku.html'
paginate_by = 60
## 参考URL https://intellectual-curiosity.tokyo/2019/02/27/djangoでlistviewを用いて検索画面を実装する方法/
def post(self, request, *args, **kwargs):
keihi_form_value = [
self.request.POST.get('workperiodYYYY', None),
self.request.POST.get('workperiodMM', None),
]
request.session['keihi_form_value'] = keihi_form_value
# 検索時にページネーションに関連したエラーを防ぐ
self.request.GET = self.request.GET.copy()
self.request.GET.clear()
return self.get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# 年月指定のデフォルトを設定しておく(今日の年月)
workperiodYYYY = gvis_today()[0:4]
workperiodMM = gvis_today()[5:7]
# sessionに値がある場合、その値をセットする。(ページングしてもform値が変わらないように)
if 'keihi_form_value' in self.request.session:
keihi_form_value = self.request.session['keihi_form_value']
workperiodYYYY = keihi_form_value[0]
workperiodMM = keihi_form_value[1]
default_data = {'workperiodYYYY': workperiodYYYY, # 経費年
'workperiodMM': workperiodMM, # 経費月
}
find_form = GvisKeihiForm(initial=default_data) # 検索フォーム
context['find_form'] = find_form
## 集計結果をセットする
## 参考URL
## https://note.com/marketscience/n/nf02a49c65554
## 年月で対象を絞る
object_list = GvisKeihi.objects.filter(
workperiod__range = [ gvis_startdayOfMonth(workperiodYYYY,workperiodMM),
gvis_finaldayOfMonth(workperiodYYYY,workperiodMM)
]
)
## 経費合計の取得がどうしてもうまく行かないので参照sql直接発行
### KingakuGokei = object_list.filter(Q(get_keihitaisho=1)).aggregate(sum=Sum('kng'))['sum']
KingakuGokei = gv_Func_GetKeihi_ofMonth(workperiodYYYY,workperiodMM,self.request)
## 口座から現金移動(元入含)合計の取得
GeninIdou = object_list.filter(Q(kamoku='002-元入金') | Q(kamoku='032-現金移動')).aggregate(sum=Sum('kng'))['sum']
## 医療費控除合計の取得
iryo1 = object_list.filter(Q(kamoku='004-医療費控除')).aggregate(sum=Sum('kng'))['sum']
## 医療費控除(貯金切り崩し)合計の取得
iryo2 = object_list.filter(Q(kamoku='033-医療費控除(貯金切り崩し)')).aggregate(sum=Sum('kng'))['sum']
## 国民健康保険合計の取得
kokumin = object_list.filter(Q(kamoku='034-国民健康保険')).aggregate(sum=Sum('kng'))['sum']
## 市税合計の取得
shizei = object_list.filter(Q(kamoku='035-市税')).aggregate(sum=Sum('kng'))['sum']
## 固定資産税合計の取得
kotei = object_list.filter(Q(kamoku='036-固定資産税')).aggregate(sum=Sum('kng'))['sum']
## 売上合計の取得
uriage = object_list.filter(Q(kamoku='101-現金売上') | Q(kamoku='102-預金売上')).aggregate(sum=Sum('kng'))['sum']
context.update({
'sum01': KingakuGokei, # 経費合計
'sum02': GeninIdou, # 口座から現金移動(元入含)
'sum03': iryo1, # 医療費控除合計
'sum04': iryo2, # 医療費控除(貯金切り崩し)合計
'sum05': kokumin, # 国民健康保険合計
'sum06': shizei, # 市税
'sum07': kotei, # 固定資産税
'sum08': uriage, # 売上合計
'sum09': object_list.count(), # 件数
})
return context
## 参考URL https://noumenon-th.net/programming/2019/12/18/django-search/
## https://codelab.website/django-queryset-filter/
## https://qiita.com/Hyperion13fleet/items/1a0369f4f5d523be5870
def get_queryset(self):
# sessionに値がある場合、その値でクエリ発行する。
# シリアル種別はicontains(部分一致の大文字小文字区別なし)で検索する
if 'keihi_form_value' in self.request.session:
keihi_form_value = self.request.session['keihi_form_value']
workperiodYYYY = keihi_form_value[0]
workperiodMM = keihi_form_value[1]
## まずは全件取得
object_list = GvisKeihi.objects.all()
## 年月で絞って日付が新しい順に並べ替え
object_list = GvisKeihi.objects.filter(
workperiod__range = [ gvis_startdayOfMonth(workperiodYYYY,workperiodMM),
gvis_finaldayOfMonth(workperiodYYYY,workperiodMM)
]
).order_by('workshubetsu','keihi_date','kamoku','workpriority')
## ログ出力
key = workperiodYYYY + "-" + workperiodMM
gv_logwrite('info',self.request,'dj-経費検索','(' + key + ')')
return object_list
else:
# 何も返さない
return GvisKeihi.objects.none()
djangoので発行したいsql
A5sqlで下書きしてデータが呼び出せることを確認しながら作った。
select sum(Kng)
from (
select
GVIS_keihi.Kamoku,
GVIS_keihi.Tehai,
GVIS_keihi.Kng,
(
select keihitaisho
from GVIS_mst_kamoku
where concat(concat(LPAD(GVIS_mst_kamoku.kamokuNo,3,'0'),'-'),GVIS_mst_kamoku.KamokuName) = GVIS_keihi.Kamoku
) as keihitaisho
from GVIS_keihi
WHERE year(GVIS_keihi.workPeriod) = 2022
and month(GVIS_keihi.workPeriod) = 1
) as q
where keihitaisho = 1 ;
djangoの自前関数でsql発行してる箇所
どこで見たか忘れたけど、sqlを直接発行したときに何らかのエラーが帰ってきてもdjangoでは普通にexceptionが発生する。
exceptionってどう拾うんやろ。
探してみたら書いてる方がおられた。
作者さんありがとう。
django4使ってるからURLに4って指定したらチュートリアル出てくるかなtって思ったら3までしかなかった。
とりあえず、sqlを発行する箇所はtry~Exceptionで囲んでおく。
ExceptionにひっかかったらエラーログがmariaDBに書かれるようにしておいた。
これだけじゃ不十分なのかもしれんけど、気づいたら書き足すかな。
def gv_Func_GetKeihi_ofMonth(year, month, arg3):
keihi = 0
## モデルを使わずにsql発行
try:
con = connections['default']
cursor = con.cursor()
SQLstr = ""
SQLstr += "select sum(Kng) "
SQLstr += "from ( "
SQLstr += " select "
SQLstr += " GVIS_keihi.Kamoku, "
SQLstr += " GVIS_keihi.Tehai, "
SQLstr += " GVIS_keihi.Kng, "
SQLstr += " ( "
SQLstr += " select keihitaisho "
SQLstr += " from GVIS_mst_kamoku "
SQLstr += " where concat(concat(LPAD(GVIS_mst_kamoku.kamokuNo,3,'0'),'-'),GVIS_mst_kamoku.KamokuName) "
SQLstr += " = GVIS_keihi.Kamoku "
SQLstr += " ) as keihitaisho "
SQLstr += " from GVIS_keihi "
SQLstr += " WHERE year(GVIS_keihi.workPeriod) = " + year
if int(month) > 0 :
SQLstr += " and month(GVIS_keihi.workPeriod) = " + month
SQLstr += ") as q "
SQLstr += "where keihitaisho = 1 "
cursor.execute(SQLstr)
ret = cursor.fetchall()
keihi = 0 if ret[0][0] is None else ret[0][0]
except Exception as e:
key = year + month
gv_logwrite('error',arg3,'dj-経費検索','key=(' + key + ')' + 'exeption={' + str(e) + '}')
## 指定年月の経費を返す
return keihi
たとえばsqlのselect
をserect
とかしておいてわざとエラー発生させたときのログはこう見える。
arg3
で渡ってきた情報から、IPアドレスとかブラウザの情報が表示できてる。
ちゃんと「SQLの文法間違ってんで」って注意してくれてる。
djangoのtemplatesで表示してる箇所
けっこう長いやん。
日付が土日の箇所は紫色で表示。
経費種別で背景色を変えてるけど、経費種別テーブルの色が入っている箇所を参照させるの忘れてるから今度直しとくか。
金額は小数点以下3桁まで持たせてるから、表示はhumanizeを使って整数表示とカンマ表示ができてる。
{% extends 'gvisWebApp/base.html' %}
{% load static %}
{% load humanize %} {# 数字の値を3桁のカンマ区切りで表示したいため追記 #}
{% load gvis_tags %} {# templatetagフォルダにカスタムタグ作って使うために追記 #}
{% block content %}
<center>
<h1>検索条件</h1>
<form method="POST">
{% csrf_token %}
<fieldset style="width:60%">
<legend align="center">検索フォーム</legend>
{% for field in find_form %}
{{ field.label }}:{{ field }}
<br>
{% endfor %}
<input class="gvis_button" type="submit" id="submit1" name="submit1" value="検索">
</fieldset>
</form>
<br>
{% if messages %}
{% for message in messages %}
<h2> {{ message }}</h2>
{% endfor %}
{% endif %}
<h1>検索結果</h1>
<fieldset style="width:90%">
{% if object_list|length == 0 %}
<p>検索結果が存在しません。</p>
{% else %}
{% if is_paginated %}
{% include 'gvisWebApp/pagination.html' %}
{% endif %}
<table border=4 align=center width="100%">
<tr>
<th>年月</th>
<th>編集</th>
<th>操作</th>
<th>経費種別</th>
<th>経費No</th>
<th>経費日付</th>
<th>科目</th>
<th>手配方法・名称</th>
<th>金額</th>
<th>備考</th>
</tr>
{% for GvisKeihi in object_list %}
<tr>
<td>{{GvisKeihi.get_yyyymm}}</td>
<td>
{# <a href="{% url 'gvisWebApp:gvis_404_KatsudoUpdate' GvisKeihi.pk %}">編集</a> #}
<a><br><a>
<a><br><a>
{# <a href="{% url 'gvisWebApp:gvis_403_KatsudoDelete' GvisKeihi.pk %}">削除</a> #}
</td>
<td>
{% comment %}
<form id="CPform" name="CPform" method="GET" style="text-align:left"
action="{% url 'gvisWebApp:GvisKeihiCopy' %}" onSubmit="return copyPopup(); " >
<input class="darkmode-ignore" type="submit" name="COPY_btn" value="行コピー">
<input type="hidden" name="yyyymm" value="{{GvisKeihi.get_yyyymm|slice:"0:4"}}{{GvisKeihi.get_yyyymm|slice:"5:7"}}">
<input type="hidden" name="pr" value="{{ GvisKeihi.workpriority }}">
</form>
{% endcomment %}
</td>
{% if GvisKeihi.workshubetsu|stringformat:"s" == "1yan" %}
<td bgcolor="#E9967A"><font color="#ffffff">{{GvisKeihi.get_workshubetsu}}</font></td>
{% elif GvisKeihi.workshubetsu|stringformat:"s" == "2kgc" %}
<td bgcolor="#8FBC8F"><font color="#ffffff">{{GvisKeihi.get_workshubetsu}}</font></td>
{% elif GvisKeihi.workshubetsu|stringformat:"s" == "3ikk" %}
<td bgcolor="#00CED1"><font color="#ffffff">{{GvisKeihi.get_workshubetsu}}</font></td>
{% else %}
<td>{{GvisKeihi.workshubetsu}}{{GvisKeihi.get_workshubetsu}}</td>
{% endif %}
<td>{% gvis_zeropad4 GvisKeihi.workpriority %}</td>
{% if GvisKeihi.get_keihi_date_dayofweek|stringformat:"s" == "Sat" %}
<td bgcolor="#ff1493"><font color="#ffffff">{{GvisKeihi.get_keihi_date}}</font></td>
{% elif GvisKeihi.get_keihi_date_dayofweek|stringformat:"s" == "Sun" %}
<td bgcolor="#ff1493"><font color="#ffffff">{{GvisKeihi.get_keihi_date}}</font></td>
{% else %}
<td>{{GvisKeihi.get_keihi_date}}</td>
{% endif %}
<td>{{GvisKeihi.kamoku }}</td>
<td>{{GvisKeihi.tehai}}</td>
<td align=right>{{GvisKeihi.kng|floatformat|intcomma }}円</td>
<td>{{GvisKeihi.biko}}</td>
</tr>
{% endfor %}
<tr align=right bgcolor="#cccccc">
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>
{% if sum01 %}
経費合計={{sum01|floatformat|intcomma}}円 <br>
{% endif %}
{% if sum02 %}
口座から現金移動(元入含)={{sum02|floatformat|intcomma}}円 <br>
{% endif %}
{% if sum03 %}
医療費控除合計 ={{sum03|floatformat|intcomma}}円 <br>
{% endif %}
{% if sum04 %}
医療費控除(貯金切り崩し)合計 ={{sum04|floatformat|intcomma}}円 <br>
{% endif %}
{% if sum05 %}
国民健康保険 ={{sum05|floatformat|intcomma}}円 <br>
{% endif %}
{% if sum06 %}
市税 ={{sum06|floatformat|intcomma}}円 <br>
{% endif %}
{% if sum07 %}
固定資産税 ={{sum07|floatformat|intcomma}}円 <br>
{% endif %}
{% if sum08 %}
売上合計 ={{sum08|floatformat|intcomma}}円 <br>
{% endif %}
</td>
<td>件数={{sum09|intcomma}}件</td>
</tr>
</table>
{% if is_paginated %}
{% include 'gvisWebApp/pagination.html' %}
{% endif %}
{% endif %}
</fieldset>
pythonでいつか改善してくれんかなぁ
modelsで書いた実テーブルにない外部参照みたいな定義をフィルタに入れられるようにしてくれんかなぁ。
あと、できたらついでにC言語にあるswitch~caseが欲しい。
if/elifで条件書いたら、インデントがズレて条件の記述開始位置が変わるからソースの見た目が気持ちわるい。