views.py
from django.db.models import Sum
from django.db.models.functions import TruncMonth
month_sales = Person.objects.annotate(
month=TruncMonth('sales__date')).annotate(total_sales=Sum('sales__sale')
).values('month', 'name','total_sales').order_by('month')
print(month_sales)
説明
Step 1Trunc関数の基本
Djangoでは、Trunc関数を使用して日付時間フィールドを特定の精度(年、月、週、日など)で切り捨てて集計することができます。これにより、時系列データの集計や分析が容易になります。
from django.db.models.functions import TruncMonth, TruncYear, TruncWeek, TruncDay, TruncHour
これらの関数は通常、annotateメソッドと一緒に使用されます。
Step 2TruncMonthの使用例
TruncMonthを使用すると、日付データを月ごとにまとめることができます:
from django.db.models.functions import TruncMonth
from django.db.models import Sum
# 月ごとの売上集計
monthly_sales = Person.objects.annotate(
month=TruncMonth('sales__date')
).values('month').annotate(
total_sales=Sum('sales__sale')
).order_by('month')
この例では、annotateでPersonモデルのmonthフィールドにsales__dateを月でまとめたもの(1日から31日までのデータが当月の1日としてまとめられる)を、total_salesには月でまとめたもののsaleの合計が代入されます。
Step 3様々な時間単位でのTrunc関数
他の時間単位でも同様にデータをまとめることができます:
from django.db.models.functions import TruncYear, TruncWeek, TruncDay, TruncHour
from django.db.models import Sum
# 年ごとの集計
yearly_sales = Person.objects.annotate(
year=TruncYear('sales__date')
).values('year').annotate(
total_sales=Sum('sales__sale')
).order_by('year')
# 週ごとの集計
weekly_sales = Person.objects.annotate(
week=TruncWeek('sales__date')
).values('week').annotate(
total_sales=Sum('sales__sale')
).order_by('week')
# 日ごとの集計
daily_sales = Person.objects.annotate(
day=TruncDay('sales__date')
).values('day').annotate(
total_sales=Sum('sales__sale')
).order_by('day')
# 時間ごとの集計
hourly_sales = Person.objects.annotate(
hour=TruncHour('sales__date')
).values('hour').annotate(
total_sales=Sum('sales__sale')
).order_by('hour')
このように、TruncYearにすると年ごと、TruncWeekにすると週ごと、TruncDayにすると日ごと、TruncHourにすると1時間ごとの集計が可能です。
Step 4複数の集計関数との組み合わせ
Trunc関数と複数の集計関数を組み合わせることもできます:
from django.db.models.functions import TruncMonth
from django.db.models import Sum, Avg, Count, Max, Min
monthly_stats = Person.objects.annotate(
month=TruncMonth('sales__date')
).values('month').annotate(
total_sales=Sum('sales__sale'),
avg_sale=Avg('sales__sale'),
sales_count=Count('sales'),
max_sale=Max('sales__sale'),
min_sale=Min('sales__sale')
).order_by('month')
この例では、月ごとの売上合計、平均売上、売上件数、最高売上、最低売上を一度に計算しています。
Step 5フィルタリングとの組み合わせ
Trunc関数はフィルタリングと組み合わせることもできます:
from django.db.models.functions import TruncMonth
from django.db.models import Sum
from datetime import datetime
# 今年の月別売上
current_year = datetime.now().year
monthly_sales_this_year = Person.objects.annotate(
month=TruncMonth('sales__date')
).values('month').annotate(
total_sales=Sum('sales__sale')
).filter(
month__year=current_year
).order_by('month')
# 特定の担当者の月別売上
sales_by_person = Person.objects.filter(
name='山田太郎'
).annotate(
month=TruncMonth('sales__date')
).values('month').annotate(
total_sales=Sum('sales__sale')
).order_by('month')
Step 6実践的な使用例
views.pyでのTrunc関数の使用例:
from django.shortcuts import render
from django.db.models.functions import TruncMonth, TruncYear
from django.db.models import Sum, Count
from .models import Person, Sales
from datetime import datetime
def sales_report(request):
# 期間フィルターの取得(デフォルトは今年)
year = request.GET.get('year', datetime.now().year)
# 月別売上データ
monthly_data = Person.objects.annotate(
month=TruncMonth('sales__date')
).values('month').annotate(
total_sales=Sum('sales__sale'),
sales_count=Count('sales')
).filter(
month__year=year
).order_by('month')
# 年別売上データ(過去5年分)
yearly_data = Person.objects.annotate(
year=TruncYear('sales__date')
).values('year').annotate(
total_sales=Sum('sales__sale'),
sales_count=Count('sales')
).order_by('-year')[:5]
# 担当者別・月別の売上データ
person_monthly_data = Person.objects.annotate(
month=TruncMonth('sales__date')
).values('name', 'month').annotate(
total_sales=Sum('sales__sale')
).filter(
month__year=year
).order_by('name', 'month')
return render(request, 'sales/report.html', {
'monthly_data': monthly_data,
'yearly_data': yearly_data,
'person_monthly_data': person_monthly_data,
'selected_year': year
})
テンプレートでの使用例(report.html):
<h1>売上レポート({{ selected_year }}年)</h1>
<form method="get">
<label for="year">年を選択:</label>
<select name="year" id="year" onchange="this.form.submit()">
{% for y_data in yearly_data %}
<option value="{{ y_data.year|date:'Y' }}" {% if y_data.year|date:'Y' == selected_year %}selected{% endif %}>
{{ y_data.year|date:'Y' }}年
</option>
{% endfor %}
</select>
</form>
<h2>月別売上</h2>
<table>
<tr>
<th>月</th>
<th>売上合計</th>
<th>売上件数</th>
</tr>
{% for data in monthly_data %}
<tr>
<td>{{ data.month|date:'Y年m月' }}</td>
<td>{{ data.total_sales|floatformat:0 }}円</td>
<td>{{ data.sales_count }}件</td>
</tr>
{% empty %}
<tr>
<td colspan="3">データがありません</td>
</tr>
{% endfor %}
</table>
<h2>担当者別・月別売上</h2>
{% regroup person_monthly_data by name as person_data %}
{% for person in person_data %}
<h3>{{ person.grouper }}</h3>
<table>
<tr>
<th>月</th>
<th>売上合計</th>
</tr>
{% for data in person.list %}
<tr>
<td>{{ data.month|date:'Y年m月' }}</td>
<td>{{ data.total_sales|floatformat:0 }}円</td>
</tr>
{% endfor %}
</table>
{% endfor %}
重要ポイント:
- Trunc関数は、時系列データの分析や集計に非常に便利です。
- 使用できる主なTrunc関数には、TruncYear、TruncQuarter、TruncMonth、TruncWeek、TruncDay、TruncHour、TruncMinute、TruncSecondがあります。
- values()メソッドと組み合わせることで、グループ化した集計が可能になります。
- DateField、DateTimeFieldの両方に対して使用できますが、時間単位の関数(TruncHourなど)はDateTimeFieldにのみ使用できます。
- 集計結果は、データベースのタイムゾーン設定に依存します。Djangoの設定(TIME_ZONE)と一致するように注意してください。