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

Django视图设置永不缓存仍失效,需重启Gunicorn才显示新数据

问题描述

我有一个部署在Linux服务器、由NGINX提供服务的Django应用。其中名为DashboardView的视图展示PostgreSQL数据库中的数据,该数据库会被独立于Django应用的进程定期更新。为了始终显示最新数据,我将该视图设置为永不缓存,但在浏览器中查看时该设置似乎未生效,只有重启服务器上的GUNICORN才能强制视图显示更新后的数据。查看Google Chrome开发者工具的网络详情,缓存控制头设置正确:max-age=0, no-cache, no-store, must-revalidate, private。请问问题是否与CBV的get_context_data方法有关?目前不知道下一步该排查什么,会话ID在GUNICORN重启后似乎没有变化,因此我认为问题与此无关。请问问题可能出在哪里?

相关代码

from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache


class NeverCacheMixin(object):
    @method_decorator(never_cache)
    def dispatch(self, *args, **kwargs):
        return super(NeverCacheMixin, self).dispatch(*args, **kwargs)


class DashboardView(NeverCacheMixin, TemplateView):
        
    template_name = "example/dashboard/dashboard.html"
     
    # product queryset   
    # get the first and last date in the data set, and derive all other dates from it    
    queryset_product_all = ViewDashboard.objects.all()
  
    first_product_date = queryset_product_all.order_by("position_date").values('position_date').first()['position_date']     
    latest_product_date = ViewDashboard.objects.values("position_date").aggregate(latest_product_date=Max('position_date'))['latest_product_date']   
    latest_product_date_str = latest_product_date.strftime("%Y-%m-%d")      
    start_itd_str = first_product_date.strftime("%Y-%m-%d")  
    start_mth_str = date(latest_product_date.year, latest_product_date.month, 1).strftime("%Y-%m-%d")
    start_year_str = date(latest_product_date.year, 1, 1).strftime("%Y-%m-%d")  

    # determine qtd start
    if latest_product_date.month in [1, 2, 3]:
        qtd_mth = 1
    elif latest_product_date.month in [4, 5, 6]:
        qtd_mth = 4
    elif latest_product_date.month in [7, 8, 9]:
        qtd_mth = 7
    elif latest_product_date.month in [10, 11, 12]:
        qtd_mth = 10

    start_qtd_str = date(latest_product_date.year, qtd_mth, 1).strftime("%Y-%m-%d")
       
    queryset_portfolio = ViewDashboard.objects.values('portfolio').annotate(
      base_lmv=Sum(Case(
        When(position_date__range=[latest_product_date_str, latest_product_date_str], then=F('base_lmv')),
        default=Value(0),
        output_field=CharField(),
    )),    
       day_product=Sum(Case(
        When(position_date__range=[latest_product_date_str, latest_product_date_str], then=F('base_daily_product')),
        default=Value(0),
        output_field=CharField(),
    )),        mtd_product=Sum(Case(
        When(position_date__range=[start_mth_str, latest_product_date_str], then=F('base_daily_product')),
        default=Value(0),
        output_field=CharField(),
    )),
      qtd_product=Sum(Case(
          When(position_date__range=[start_qtd_str, latest_product_date_str], then=F('base_daily_product')),
          default=Value(0),
          output_field=CharField(),
      )),
      ytd_product=Sum(Case(
        When(position_date__range=[start_year_str, latest_product_date_str], then=F('base_daily_product')),
        default=Value(0),
        output_field=CharField(),
    )),
     itd_product=Sum(Case(
        When(position_date__range=[start_itd_str, latest_product_date_str], then=F('base_daily_product')),
        default=Value(0),
        output_field=CharField(),
    ))
    )           
   
    product_portfolio = SummaryproductPortfolioTable(queryset_portfolio)   
        

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)    

        context['latest_product_date'] = self.latest_product_date          
        context['product_portfolio'] = self.product_portfolio          
        
        return context
        

class ViewDashboard(models.Model):   
    unqiue_id = models.IntegerField(primary_key=True)    
    partners_id = models.CharField(max_length=50)
    base_daily_product = models.FloatField(default=0)
    position_date = models.DateField()    
    product_timestamp = models.DateTimeField()
    book_id = models.IntegerField()
    book = models.CharField(max_length=100, blank=True, null=True)
    portfolio = models.CharField(max_length=100, blank=True, null=True)
    analyst = models.CharField(max_length=100)
    base_lmv = models.FloatField(default=0)
   
    class Meta: 
        managed = False       
        db_table = "view_dashboard_product_exposures"
问题根源与解决方法

问题完全不在get_context_data方法,而是你把数据库查询和计算逻辑写在了DashboardView类的类属性里,这些代码只会在类第一次加载时执行一次——也就是Gunicorn启动的时候。后续每次请求进来,视图不会重新执行这些查询,而是直接复用类加载时生成的旧数据,这就是为什么只有重启Gunicorn才能看到新数据。

修复步骤

  1. 把所有类属性里的数据库查询、日期计算、聚合逻辑,全部移到get_context_data方法内部,或者单独的实例方法中。这样每次请求进来时,都会重新执行查询,获取最新数据。
  2. 移除类级别的queryset_product_all、first_product_date、latest_product_date、queryset_portfolio、product_portfolio等属性,只保留template_name这类静态配置。

修复后的核心代码片段:

class DashboardView(NeverCacheMixin, TemplateView):
    template_name = "example/dashboard/dashboard.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)    

        # 所有数据查询和计算都移到这里
        queryset_product_all = ViewDashboard.objects.all()
        first_product_date = queryset_product_all.order_by("position_date").values('position_date').first()['position_date']     
        latest_product_date = ViewDashboard.objects.values("position_date").aggregate(latest_product_date=Max('position_date'))['latest_product_date']   
        latest_product_date_str = latest_product_date.strftime("%Y-%m-%d")      
        start_itd_str = first_product_date.strftime("%Y-%m-%d")  
        start_mth_str = date(latest_product_date.year, latest_product_date.month, 1).strftime("%Y-%m-%d")
        start_year_str = date(latest_product_date.year, 1, 1).strftime("%Y-%m-%d")  

        # determine qtd start
        if latest_product_date.month in [1, 2, 3]:
            qtd_mth = 1
        elif latest_product_date.month in [4, 5, 6]:
            qtd_mth = 4
        elif latest_product_date.month in [7, 8, 9]:
            qtd_mth = 7
        elif latest_product_date.month in [10, 11, 12]:
            qtd_mth = 10

        start_qtd_str = date(latest_product_date.year, qtd_mth, 1).strftime("%Y-%m-%d")
       
        queryset_portfolio = ViewDashboard.objects.values('portfolio').annotate(
          base_lmv=Sum(Case(
            When(position_date__range=[latest_product_date_str, latest_product_date_str], then=F('base_lmv')),
            default=Value(0),
            output_field=CharField(),
        )),    
           day_product=Sum(Case(
            When(position_date__range=[latest_product_date_str, latest_product_date_str], then=F('base_daily_product')),
            default=Value(0),
            output_field=CharField(),
        )),       
        # ... 其他聚合字段保持不变
        )           
   
        product_portfolio = SummaryproductPortfolioTable(queryset_portfolio)   

        context['latest_product_date'] = latest_product_date          
        context['product_portfolio'] = product_portfolio          
        
        return context

额外注意点

  • 类属性在Python中是类级别的共享变量,一旦加载就不会自动刷新,务必把动态数据查询放在实例方法(比如get、get_context_data)里。
  • 检查你的SummaryproductPortfolioTable是否有缓存逻辑,如果它本身会缓存传入的查询集结果,也要确保每次请求都创建新的实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 13:10:55