You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django:长期存储数据库查询变量并生成图表展示

实现Django仪表盘的长期查询存储与图表展示

嘿,针对你正在构建的合同管理仪表盘,我把需求拆成两个核心部分来给你讲清楚:

一、长期存储数据库查询变量

这里有两种常用方案,你可以根据实际需求选择:

1. 使用Django缓存框架(适合频繁查询、无需持久化的场景)

Django自带的缓存系统能帮你把高频统计的查询结果缓存起来,避免每次请求都重复跑数据库。比如总合同数、即将到期的合同数这类数据就很适合用缓存:

from django.core.cache import cache
from .models import Contract
from datetime import datetime, timedelta

def get_contract_dashboard_stats():
    # 先尝试从缓存读取数据
    cached_stats = cache.get("contract_dashboard_core_stats")
    if not cached_stats:
        # 执行数据库查询
        total_contracts = Contract.objects.count()
        expiring_soon = Contract.objects.filter(
            contract_expires__lte=datetime.now() + timedelta(days=30),
            contract_expires__isnull=False
        ).count()
        # 统计过去6个月的新增合同数
        monthly_new_contracts = []
        for i in range(6):
            target_month = datetime.now() - timedelta(days=i*30)
            count = Contract.objects.filter(
                contract_date__year=target_month.year,
                contract_date__month=target_month.month
            ).count()
            monthly_new_contracts.append({
                "month": target_month.strftime("%b"),
                "count": count
            })
        # 组装统计数据
        cached_stats = {
            "total_contracts": total_contracts,
            "expiring_soon": expiring_soon,
            "monthly_new": monthly_new_contracts
        }
        # 缓存24小时(可根据需求调整过期时间)
        cache.set("contract_dashboard_core_stats", cached_stats, 60*60*24)
    return cached_stats

2. 用模型持久化存储(适合需要长期保留、可追溯的统计数据)

如果需要把统计数据永久存在数据库里(比如要查看历史统计变化),可以创建一个专门的统计模型,再配合定时任务定期更新:

第一步:创建统计模型

from django.db import models

class DashboardContractStats(models.Model):
    stat_identifier = models.CharField(max_length=60, unique=True)  # 比如"total_contracts"
    numeric_value = models.IntegerField()
    recorded_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = "合同仪表盘统计"
        verbose_name_plural = "合同仪表盘统计"

    def __str__(self):
        return f"{self.stat_identifier}: {self.numeric_value}"

第二步:编写定时更新任务

用Celery或者Django Q这类任务调度工具,定期计算并更新统计数据。这里以Celery为例:

from celery import shared_task
from .models import Contract, DashboardContractStats
from datetime import datetime, timedelta

@shared_task
def refresh_contract_dashboard_stats():
    # 更新总合同数
    total_count = Contract.objects.count()
    DashboardContractStats.objects.update_or_create(
        stat_identifier="total_contracts",
        defaults={"numeric_value": total_count}
    )
    # 更新即将到期的合同数
    expiring_count = Contract.objects.filter(
        contract_expires__lte=datetime.now() + timedelta(days=30),
        contract_expires__isnull=False
    ).count()
    DashboardContractStats.objects.update_or_create(
        stat_identifier="expiring_soon",
        defaults={"numeric_value": expiring_count}
    )
    # 其他统计项同理扩展

然后在Celery配置里设置定时任务(比如每天凌晨1点执行):

from celery.schedules import crontab

CELERY_BEAT_SCHEDULE = {
    'refresh-contract-stats-daily': {
        'task': 'your_app.tasks.refresh_contract_dashboard_stats',
        'schedule': crontab(hour=1, minute=0),
    },
}

二、将数据以图表形式展示

前端图表库是最佳选择,推荐用Chart.js(轻量易用)或者ECharts(功能强大)。这里以Chart.js结合Django模板为例:

1. 在视图里传递数据到模板

from django.shortcuts import render
from .utils import get_contract_dashboard_stats  # 前面的缓存函数

def contract_dashboard(request):
    stats = get_contract_dashboard_stats()
    return render(request, "dashboard/contract_dashboard.html", {
        "total_contracts": stats["total_contracts"],
        "expiring_soon": stats["expiring_soon"],
        "monthly_new_data": stats["monthly_new"]
    })

2. 在模板里渲染图表

<!-- dashboard/contract_dashboard.html -->
<div class="chart-container" style="width: 80%; margin: 0 auto;">
    <canvas id="monthlyNewContracts"></canvas>
</div>

<!-- 引入Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
// 从模板获取数据
const monthlyData = {{ monthly_new_data|safe }};

// 初始化图表
const ctx = document.getElementById('monthlyNewContracts').getContext('2d');
new Chart(ctx, {
    type: 'bar',
    data: {
        labels: monthlyData.map(item => item.month),
        datasets: [{
            label: '每月新增合同数',
            data: monthlyData.map(item => item.count),
            backgroundColor: 'rgba(75, 192, 192, 0.6)',
            borderColor: 'rgba(75, 192, 192, 1)',
            borderWidth: 1
        }]
    },
    options: {
        responsive: true,
        scales: {
            y: {
                beginAtZero: true,
                title: {
                    display: true,
                    text: '合同数量'
                }
            },
            x: {
                title: {
                    display: true,
                    text: '月份'
                }
            }
        }
    }
});
</script>

小提示:修复你现有Contract模型的小问题

你的save方法里如果contract_expires为null会报错,建议加个判断:

def save(self, *args, **kwargs):
    if self.contract_date and self.contract_expires:
        self.contract_length = (self.contract_expires - self.contract_date).days
    else:
        self.contract_length = None  # 或者设置一个默认值
    super().save(*args, **kwargs)

内容的提问来源于stack exchange,提问作者Preben Hesvik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:21:35