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才能看到新数据。
修复步骤
- 把所有类属性里的数据库查询、日期计算、聚合逻辑,全部移到
get_context_data方法内部,或者单独的实例方法中。这样每次请求进来时,都会重新执行查询,获取最新数据。 - 移除类级别的
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
相关产品推荐
相关产品推荐

