Django TemplateView传Dict报500错误,求ListView/DetailView解决方案
问题修复与优化方案
一、当前问题的核心原因
- 模板变量缺失:
BucketView未将coins变量传入模板上下文,导致模板中{% for coin in coins %}循环触发变量未定义错误,这是500服务器错误的主要诱因。 - Django模板语法限制:Django模板不支持
prices[coin]这类直接通过变量键取值的写法——coin是Coin对象,即使__str__返回ticker,模板也不会自动将其转换为字典的字符串键;同时prices.get(coin, 'N/A')也无法直接生效,因为模板不支持传递变量作为字典get方法的参数。 - API调用无异常处理:循环调用Yahoo Finance API时未做异常捕获,若代币代码无效或请求失败,会直接抛出异常导致500错误。
二、修复步骤
1. 修正View层代码
补充coins变量到上下文,同时优化价格获取逻辑并增加异常处理,还可直接给Coin对象附加价格属性,避免模板处理字典取值:
class BucketView(LoginRequiredMixin, TemplateView): template_name = 'coinbucket/bucket.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) coins = Coin.objects.all() for coin in coins: try: ticker = yf.Ticker(coin.ticker) price = ticker.history(period='1d')['Close'][0] coin.price = f'$ {price:.6f}' except (IndexError, Exception): # 处理API请求失败或无数据的情况 coin.price = 'N/A' context["coins"] = coins return context
2. 简化模板代码
现在模板可直接调用coin.price,无需处理字典:
{% for coin in coins %} <div class="col-12 col-md-8 my-2"> <div class="card"> <div class="card-body"> <div class="row"> <div class="col">{{ coin.ticker }}</div> <div class="col">{{ coin.price }}</div> </div> </div> </div> </div> {% endfor %}
备选方案:自定义模板过滤器(保留prices字典场景)
若坚持使用prices字典,可自定义模板过滤器实现变量键取值:
- 在app目录下创建
templatetags文件夹,新建custom_filters.py:
from django import template register = template.Library() @register.filter def get_item(dictionary, key): return dictionary.get(key, 'N/A')
- 模板中加载过滤器并使用:
{% load custom_filters %} {% for coin in coins %} <div class="col-12 col-md-8 my-2"> <div class="card"> <div class="card-body"> <div class="row"> <div class="col">{{ coin.ticker }}</div> <div class="col">{{ prices|get_item:coin.ticker }}</div> </div> </div> </div> </div> {% endfor %}
同时确保View中传入coins和prices(记得给prices的获取逻辑加异常处理)。
三、ListView+DetailView实现建议
1. ListView实现(代币列表页)
用ListView替代TemplateView,贴合Django列表展示规范,同时注入价格数据:
class CoinListView(LoginRequiredMixin, ListView): model = Coin template_name = 'coinbucket/coin_list.html' context_object_name = 'coins' def get_queryset(self): queryset = super().get_queryset() for coin in queryset: try: ticker = yf.Ticker(coin.ticker) price = ticker.history(period='1d')['Close'][0] coin.current_price = f'$ {price:.6f}' except (IndexError, Exception): coin.current_price = 'N/A' return queryset
模板直接遍历coins并调用coin.current_price即可。
2. DetailView实现(代币详情页)
展示单个代币的详细价格信息(如历史走势、涨跌幅):
class CoinDetailView(LoginRequiredMixin, DetailView): model = Coin template_name = 'coinbucket/coin_detail.html' context_object_name = 'coin' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) coin = self.get_object() try: ticker = yf.Ticker(coin.ticker) # 获取近7天价格数据 hist = ticker.history(period='7d') context['price_history'] = hist.reset_index().values.tolist() # 当前价格 context['current_price'] = f'$ {hist["Close"].iloc[-1]:.6f}' # 24小时涨跌幅 context['daily_change'] = f'{((hist["Close"].iloc[-1] - hist["Close"].iloc[-2])/hist["Close"].iloc[-2])*100:.2f}%' except Exception: context['price_history'] = [] context['current_price'] = 'N/A' context['daily_change'] = 'N/A' return context
模板可展示核心数据,还可结合Chart.js等工具渲染历史价格走势:
<div class="card"> <div class="card-body"> <h2>{{ coin.ticker }}</h2> <p>当前价格:{{ current_price }}</p> <p>24小时涨跌幅:{{ daily_change }}</p> <!-- 此处可添加Chart.js图表渲染price_history数据 --> </div> </div>
额外优化建议
- 缓存价格数据:Yahoo Finance API有请求频率限制,且实时请求会拖慢页面,建议用Django缓存将价格数据缓存15-30分钟,避免重复请求。
- 异步任务处理:若代币数量较多,API请求耗时久,可使用Celery等异步框架在后台更新价格,而非请求时同步获取。
内容的提问来源于stack exchange,提问作者Akshay Saambram
相关产品推荐
相关产品推荐

