とりあえずdjangoの復習用に適当にテーブルを作成。
投入データは下記から取得。
日本政府が出してるっぽいデータ
from django.db import models
from datetime import datetime
# 人口テーブル
class Population(models.Model):
prefectures_code = models.CharField(max_length=10,verbose_name="都道府県コード", null=False)
prefectures = models.CharField(max_length=10,verbose_name="都道府県", null=False)
era = models.CharField(max_length=500, verbose_name="元号", null=False)
jp_calendar = models.CharField(max_length=500, verbose_name="和暦", null=False)
year = models.CharField(max_length=500, verbose_name="西暦", null=False)
population = models.CharField(max_length=500, verbose_name="人口", null=False)
man = models.CharField(max_length=500, verbose_name="男", null=False)
woman = models.CharField(max_length=500, verbose_name="女", null=False)
create_date = models.DateTimeField(default=datetime.now, null=True, verbose_name="登録日")
def __str__(self):
return self.prefectures
適当すぎて人口の数値部分も文字列で作成。
問題ないかと思いそのまま続行した結果、色々被害続出。
・tables2の復習で最新年度の人口の表を作成したが、
ソート機能が文字列(辞書順)でソートしてしまうため、正しくソートできない。
例:
123000,200,500があった場合、辞書順なので1文字目を取って
123000 < 200 < 500 になる。
・matplotlibの復習で特定の県について年ごとの人口の推移を見ようとしたが、
人口が文字列のため、グラフがちゃんとできない。
例:
1年間で100万人→120万人に増えても1メモリ分しか増加しない。
1年間で100万人→200万人に増えても1メモリ分しか増加しない。
matplotlibの方は特にハマらなかったのでtables2についてメモ。
(model直してDB作り直すのが一番ですが、勉強も兼ねてmodelはそのままで続行)
table.pyのソース
from population.models import Population
import django_tables2 as tables
from django_tables2.utils import A
from population.models import Population
class ButtonLinkColumn(tables.LinkColumn):
"""
リンク表示用カラム(ボタン)
"""
def render(self, record, value):
if record.count > 0:
# ステータス:完了
return "データ表示"
else:
# ステータス:完了以外
return ""
class PopulationTable(tables.Table):
"""
人口画面用テーブル
Population
"""
prefectures = tables.Column(verbose_name="都道府県")
# リンク用
plot = tables.LinkColumn("population:pop_plot", args=[A("prefectures_code")],
verbose_name = "",text="人口グラフ",
attrs={"a": {"class": "btn btn-success text-nowrap"}}
)
population = tables.Column(verbose_name="総人口")
man = tables.Column(verbose_name="人口(男性)")
woman = tables.Column(verbose_name="人口(女性)")
class Meta:
model = Population
template_name = 'django_tables2/bootstrap4.html'
# 表示する列column
fields = ('prefectures_code',
'prefectures',
'plot',
'population',
'man',
'woman',
)
attrs = {"class": "table table-striped"}
django-tables2の参考サイト
参考サイトを見るとorder_カラム名で対象のカラムのソート条件をいじれるらしい。
from django.db.models.functions import Length
~以下中略~
def order_name(self, queryset, is_descending):
queryset = queryset.annotate(
length=Length("first_name")
).order_by(("-" if is_descending else "") + "length")
return (queryset, True)
真似してmanでやってみると、桁数でのソートはできた。
def order_man(self, queryset, is_descending):
queryset = queryset.annotate(
length=Length("man")
).order_by(("-" if is_descending else "") + "length")
return (queryset, True)
じゃぁ次は数値でソートしたいと思ったが、ここからが問題。
理解度が低いせいかLength部分に何が入るか分からず、そこを調べるのに時間がかかった。
色々調べたが、googleでは上手く見つけられずdjango.db.models.functionsを調べることで解決した。
from .comparison import Cast, Coalesce, Greatest, Least, NullIf
from .datetime import (
Extract, ExtractDay, ExtractHour, ExtractIsoWeekDay, ExtractIsoYear,
ExtractMinute, ExtractMonth, ExtractQuarter, ExtractSecond, ExtractWeek,
ExtractWeekDay, ExtractYear, Now, Trunc, TruncDate, TruncDay, TruncHour,
TruncMinute, TruncMonth, TruncQuarter, TruncSecond, TruncTime, TruncWeek,
TruncYear,
)
from .math import (
Abs, ACos, ASin, ATan, ATan2, Ceil, Cos, Cot, Degrees, Exp, Floor, Ln, Log,
Mod, Pi, Power, Radians, Round, Sign, Sin, Sqrt, Tan,
)
from .text import (
MD5, SHA1, SHA224, SHA256, SHA384, SHA512, Chr, Concat, ConcatPair, Left,
Length, Lower, LPad, LTrim, Ord, Repeat, Replace, Reverse, Right, RPad,
RTrim, StrIndex, Substr, Trim, Upper,
)
from .window import (
CumeDist, DenseRank, FirstValue, Lag, LastValue, Lead, NthValue, Ntile,
PercentRank, Rank, RowNumber,
)
__all__ = [
# comparison and conversion
'Cast', 'Coalesce', 'Greatest', 'Least', 'NullIf',
# datetime
'Extract', 'ExtractDay', 'ExtractHour', 'ExtractMinute', 'ExtractMonth',
'ExtractQuarter', 'ExtractSecond', 'ExtractWeek', 'ExtractIsoWeekDay',
'ExtractWeekDay', 'ExtractIsoYear', 'ExtractYear', 'Now', 'Trunc',
'TruncDate', 'TruncDay', 'TruncHour', 'TruncMinute', 'TruncMonth',
'TruncQuarter', 'TruncSecond', 'TruncTime', 'TruncWeek', 'TruncYear',
# math
'Abs', 'ACos', 'ASin', 'ATan', 'ATan2', 'Ceil', 'Cos', 'Cot', 'Degrees',
'Exp', 'Floor', 'Ln', 'Log', 'Mod', 'Pi', 'Power', 'Radians', 'Round',
'Sign', 'Sin', 'Sqrt', 'Tan',
# text
'MD5', 'SHA1', 'SHA224', 'SHA256', 'SHA384', 'SHA512', 'Chr', 'Concat',
'ConcatPair', 'Left', 'Length', 'Lower', 'LPad', 'LTrim', 'Ord', 'Repeat',
'Replace', 'Reverse', 'Right', 'RPad', 'RTrim', 'StrIndex', 'Substr',
'Trim', 'Upper',
# window
'CumeDist', 'DenseRank', 'FirstValue', 'Lag', 'LastValue', 'Lead',
'NthValue', 'Ntile', 'PercentRank', 'Rank', 'RowNumber',
]
上記にあるメソッドは大体使えるっぽいのでAbs(絶対値)を使って解決した。
def order_population(self, queryset, is_descending):
queryset = queryset.annotate(
abs=Abs("population")
).order_by(("-" if is_descending else "") + "abs")
return (queryset, True)
def order_man(self, queryset, is_descending):
queryset = queryset.annotate(
abs=Abs("man")
).order_by(("-" if is_descending else "") + "abs")
return (queryset, True)
def order_woman(self, queryset, is_descending):
queryset = queryset.annotate(
abs=Abs("woman")
).order_by(("-" if is_descending else "") + "abs")
return (queryset, True)