djangoをdockerコンテナで利用(15) – djangoでsql直接発行

djangoでレコードの集計してたら、どうしてもできない集計があることに気づいた。
仕方がないので、直接sql発行して集計させることにした。

sqlを文字列として準備し発行すると、仮にexceptionが発生したときにもそれをエラーログとして出力させるようにした。

ログ出力はmariadbに出力するようにしてる。

今回はレコード集計結果を作ったのでそのメモ。

結論

djangoで経費のレコードを処理する画面を作ってる途中。

これから「編集」と「操作」の列にボタン足して編集・コピー・削除の機能をつけてこうとしてる。

django-record-summary

他の画面で既に「編集」と「操作」は作ってあるから、djangoのviewsにはコピーして作れば余裕。

経費テーブルには、かかった費用の目安を記録しときたいから、自作の科目も含んでて、実際の「水道代」とか「外食費」もつけて台帳管理してる。

自宅で業務をしてると「水道光熱費」ってのは、実生活でかかる水道代や電気代を業務と生活で按分しなきゃいけないから、全部を計上しちゃいけない。

確定申告で使う経費科目は決まっているから、自作の経費科目は経費計上しないようにしたい。

けれど、djangoではその科目に応じた集計をさせられない場面があった。

予想してたけど、やっぱりできん集計あるんやなぁ。
(自分がまだ見つけられてないだけかもしれん)

さっさと進めて作りたかったから、直接SQLの文字列を発行して集計結果を表示させるようにした。

django-record-summary

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は、科目テーブルにある科目番号と科目名が埋め込んである。

idworkPeriodworkShubetsuworkPriorityKeihi_dateKamokuTehaiKngBiko
90582022/03/01/火 0:00:001yan10012022/03/10/木 0:00:00008-給料賃金給与6000020140924申請の青色事業者専従者給与に関する届出書により10万円を目標に計上。
90592022/03/01/火 0:00:001yan10022022/03/10/木 0:00:00020-水道光熱費光熱費1000050%を事業用の電気代とする。月平均2万円x50%=1万円。
90682022/03/01/火 0:00:001yan10032022/03/10/木 0:00:00025-通信費インターネット/電話利用料10000電話とインターネットは50%を事業用の電気代とする。月平均2万円x50%=1万円。
90602022/03/01/火 0:00:002kgc10102022/03/31/木 0:00:00101-現金売上amazon販売0物販(3/xx)
90772022/03/01/火 0:00:002kgc10202022/03/10/木 0:00:00102-預金売上お客様100000xxx案件
90742022/03/01/火 0:00:002kgc10472022/03/31/木 0:00:00023-損害保険料xx保険6550エージェント会社提供の掛け捨て保険
90872022/03/01/火 0:00:002kgc10682022/03/05/土 0:00:00003-研究開発費定食屋1630IT市場調査。昼食。
90702022/03/01/火 0:00:002kgc10722022/03/06/日 0:00:00001-打合会議費喫茶xx2480月初打合せ
91012022/03/01/火 0:00:002kgc20192022/03/20/日 0:00:00003-研究開発費apple4787mp4解析用
90552022/03/01/火 0:00:003ikk10422022/03/31/木 0:00:00038-外食費外食費累積1467目安
90562022/03/01/火 0:00:003ikk10432022/03/31/木 0:00:00039-食費食費累積74120目安
90512022/03/01/火 0:00:003ikk10452022/03/31/木 0:00:00099-預金残高銀行残高0残高確定
90852022/03/01/火 0:00:003ikk10462022/03/31/木 0:00:00040-水道代大阪市水道局38421月分水道代。1月13日検針。22立米。(クレカ引き落とし)
90542022/03/01/火 0:00:003ikk10532022/03/31/木 0:00:00041-電気代関西電力197383月分電気代。919kwh。

経費種別テーブル

workShubetsuは経費の種別で、djangoのtemplatesで表示させるときの色指定が一緒に入ってる。

idkeihiNokeihiNamekeihiSetsumeifoColorbgColor
111yan家賃・固定費#ffffff#E9967A
222kgc小口現金#ffffff#8FBC8F
333ikk一括支払#ffffff#00CED1

科目テーブル

科目テーブルは一般書籍を見てかなり昔に作った。
自分にとってはほぼ普遍的なデータ。

kamokuNoは自分用に勝手に作ったもの。
kamokuNameとペアで経費テーブルに入れるとき使う。
kamokuNoが100番台は自作の科目。90番台は当月の現金と預金の残高を強制セットするために作ってあって、お釣りもらい忘れなんかで実際の残高とあわないときに使う。

hyojiOrderはdjangoのtemplatesでリストボックス表示するときの表示順が入ってる。
keihiTaishoは経費として計上する対象を1にして設定してある。

idkamokuNokamokuNamehyojiOrderkeihiTaisho
22前年繰越-元入金10
46101現金売上20
47102預金売上30
45100活動費2010
3232現金移動2020
48103現金仕入3011
49104預金仕入3021
44医療費控除10030
3333医療費控除(貯金切り崩し)10040
11打合会議費10051
33研究開発費10061
55売上原価10071
66外注工賃10081
77貸し倒れ金10091
88給料賃金10101
99減価償却費10111
1010研修費10121
1111広告宣伝費10131
1212雑費10141
1313支払手数料10151
1414支払報酬10161
1515車両関係費10171
1616修繕費10181
1717消耗品費10191
3131IT機材費10191
1818諸会費10201
1919新聞図書費10211
2020水道光熱費10221
2121接待交際費10231
2222租税公課10241
2323損害保険料10251
2424地代家賃10261
2525通信費10271
2626荷造運賃10281
2727福利厚生費10291
2828リース料10301
2929利子割引料10311
3030旅費交通費10321
3434国民健康保険80010
3535市税80020
3636固定資産税80030
3737国民年金80040
3838外食費80050
3939食費80060
4040水道代80070
4141電気代80080
4242電話代80090
4398現金残高90010
4499預金残高90020

自作の科目として、たとえば「食費」は経費に計上しちゃいけないけど、「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ってどう拾うんやろ。
探してみたら書いてる方がおられた。

作者さんありがとう。

Pythonでtry exceptの書き方と、エラー内容の取得方法
恥ずかしながら、今までPythonのプログラムを書いているとき、ちゃんとエラー処理をしていませんでした。というのも、exceptに何を記載したらよいのかいまいちわからないし、エラーを受け取った時のエラーメッセージをどのように表示すればよいか...

django4使ってるからURLに4って指定したらチュートリアル出てくるかなtって思ったら3までしかなかった。

8. Errors and Exceptions
Until now error messages haven’t been more than mentioned, but if you have tried out the examples you have probably seen...

とりあえず、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のselectserectとかしておいてわざとエラー発生させたときのログはこう見える。

django-record-summary

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で条件書いたら、インデントがズレて条件の記述開始位置が変わるからソースの見た目が気持ちわるい。

タイトルとURLをコピーしました