📅  最后修改于: 2023-12-03 15:00:26.544000             🧑  作者: Mango
在 Django 中,ORM(对象关系映射)是让我们能够用 Python 代码来操作数据库的一种方式。通过 Django ORM,我们可以很方便地使用 SQL 语言进行操作,而不用直接编写 SQL 代码。
有时候我们需要按照一定的规则对数据库的数据进行分组,例如按照月份或年份进行分组以统计数据。这篇文章将介绍如何使用 Django ORM 对数据库数据按照月份和年份进行分组。
首先,我们创建一个简单的名为 Order
的模型,来表示订单:
from django.db import models
class Order(models.Model):
order_date = models.DateField()
amount = models.DecimalField(max_digits=5, decimal_places=2)
每一条订单记录包含一个 order_date
属性表示订单的日期,和一个 amount
属性表示订单的总金额。
我们可以使用 Django 自带的 SQLite3 数据库来进行测试,因为它是一个小型的、易于部署和测试的数据库。在设置文件中,设置数据库为 SQLite3:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
我们可以使用 Django Admin 界面或 Django Shell 来生成一些数据:
import datetime
from random import randint
from myapp.models import Order
for i in range(60):
order_date = datetime.date(2021, randint(1, 12), randint(1, 28))
amount = randint(20, 60)
Order.objects.create(order_date=order_date, amount=amount)
这段代码会往数据库中插入60条记录,其中订单的日期在2021年内随机生成,金额在20到60之间随机生成。
我们可以使用 Django ORM 来按月份进行分组,代码如下:
from django.db.models.functions import ExtractMonth, TruncMonth
from django.db.models import Sum
orders_by_month = Order.objects.annotate(
month=TruncMonth('order_date')
).values(
'month'
).annotate(
total=Sum('amount')
).order_by('-month')
for item in orders_by_month:
print(f"{item['month'].strftime('%Y-%m')}: {item['total']}")
上面的代码首先用 annotate()
方法添加一个名为 month
的属性,该属性值为订单日期的月份的第一天。具体来说,它调用了 TruncMonth
函数来截取日期、月份,并只保留月份的信息。
然后,我们使用 values()
方法来指定返回的结果集只包含 month
属性。这是必须的,因为我们正在进行分组。
最后,我们使用 annotate()
和 Sum()
方法根据 month
属性对订单金额进行求和,并按照降序对 month
属性进行排序。这样我们就可以得到一个按照月份进行分组的结果集。
## 订单汇总
### 按月份汇总
| 日期 | 总金额 |
| --- | ---: |
{% for item in orders_by_month %}
| {{ item.month|date:"Y-m" }} | {{ item.total }} |
{% empty %}
没有数据。
{% endfor %}
上面是输出 Markdown 格式的结果的模板。我们在 Django 中使用模板引擎来生成最终的 Markdown 格式的文本。渲染模板的例子如下:
from django.template import Template, Context
template_string = """
## 订单汇总
### 按月份汇总
| 日期 | 总金额 |
| --- | ---: |
{% for item in orders_by_month %}
| {{ item.month|date:"Y-m" }} | {{ item.total }} |
{% empty %}
没有数据。
{% endfor %}
"""
template = Template(template_string)
context = Context({'orders_by_month': orders_by_month})
markdown_text = template.render(context)
print(markdown_text)
渲染完成后,我们就可以得到一个 Markdown 格式的文本,它包含了按月份进行分组的汇总数据。
如果我们需要按年份进行分组,则可以按照类似的方式编写代码。这里我们先创建一个名为 YearlyReport
的模型,表示每年的报告数据。
class YearlyReport(models.Model):
year = models.IntegerField()
total_amount = models.DecimalField(max_digits=10, decimal_places=2)
total_count = models.IntegerField()
我们可以使用 SQL 语句来生成数据:
INSERT INTO myapp_yearlyreport (id, year, total_amount, total_count) VALUES (1, 2021, 2306.34, 60);
INSERT INTO myapp_yearlyreport (id, year, total_amount, total_count) VALUES (2, 2020, 2101.78, 65);
INSERT INTO myapp_yearlyreport (id, year, total_amount, total_count) VALUES (3, 2019, 1842.18, 66);
INSERT INTO myapp_yearlyreport (id, year, total_amount, total_count) VALUES (4, 2018, 1745.76, 71);
然后,我们可以使用 Django ORM 来进行查询:
from django.db.models.functions import ExtractYear, TruncYear
from django.db.models import Sum, Count
reports_by_year = YearlyReport.objects.annotate(
year=TruncYear('year')
).values(
'year'
).annotate(
total_amount=Sum('total_amount'),
total_count=Count('id')
).order_by('-year')
for item in reports_by_year:
print(f"{item['year'].strftime('%Y')}: {item['total_amount']}, {item['total_count']} orders")
上面的代码与按月份进行分组的代码很相似。我们使用 annotate()
方法添加一个名为 year
的属性,该属性值为年份的第一天。然后,我们使用 values()
方法来指定返回的结果集只包含 year
属性。最后,我们使用 annotate()
和 Sum()
方法对 total_amount
属性进行求和,并对 id
属性进行计数,这样我们就可以得到一个按照年份进行分组的结果集。
最终,我们可以使用类似上面的方式来输出 Markdown 格式的文本。
在这篇文章中,我们介绍了如何使用 Django ORM 对数据库数据按照月份和年份进行分组。我们使用了 Django 内置的时间函数和聚合函数来实现这个功能。同时,我们使用了 Django 的模板引擎来生成最终的 Markdown 格式的文本。
Django ORM 是一个非常强大的工具,它使得开发者可以很方便地操作数据库。希望本文对你有所帮助,祝你在 Django 的开发之路上越走越好!