如何实现Django中评论对象与关联TV数据的同时展示?
问题
我需要展示用户评论,同时通过评论对象中的tv_id查询并展示对应的TV数据。每条评论都关联tv_id,但目前不知道如何在HTML中同时展示评论和对应的TV数据,求实现方法。
当前代码
模型定义
class TV(models.Model): id = models.CharField(primary_key=True, editable=False, validators=[alphanumeric],max_length = 9999) stars = models.FloatField( blank=False, null=False, default=0, validators=[MinValueValidator(0.0), MaxValueValidator(10.0)] ) def get_comments(self): return Comment_tv.objects.filter(tv_id=self.id) def average_stars(self): comments = self.get_comments() n_comments = comments.count() if n_comments: self.stars = sum([comment.stars for comment in comments]) / n_comments else: self.stars = 0 return self.stars class Comment_tv(models.Model): comment = models.TextField(max_length=1000) stars = models.FloatField( blank=False, null=False, default=0, validators=[MinValueValidator(0.0), MaxValueValidator(10.0)] ) user = models.ForeignKey(CustomUser, on_delete=models.CASCADE) tv = models.ForeignKey(TV, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: unique_together = ('user', 'tv') indexes = [ models.Index(fields=['user', 'tv']), ]
TMDB API调用代码
data = requests.get(f"https://api.themoviedb.org/3/tv/{tv_id}?api_key={TMDB_API_KEY}&language=en-US")
视图定义
class Comment_List_TV(ListView): template_name = 'account/user_comment_list_tv.html' def get_queryset(self): Comment_list_query = Comment_tv.objects.none() if self.request.user.is_authenticated: Comment_list_query = Comment_tv.objects.filter(user=self.request.user) return Comment_list_query
解决方案
1. 优化查询,预关联TV对象
修改视图的get_queryset方法,使用select_related预取关联的TV对象,避免N+1数据库查询问题:
class Comment_List_TV(ListView): template_name = 'account/user_comment_list_tv.html' def get_queryset(self): if self.request.user.is_authenticated: # 预取关联的TV模型数据,减少数据库查询次数 return Comment_tv.objects.filter(user=self.request.user).select_related('tv') return Comment_tv.objects.none()
2. 获取TMDB TV数据(两种可选方式)
方式一:视图批量获取(推荐,减少API调用次数)
在视图的get_context_data中批量获取所有评论对应的TMDB数据,并存入上下文供模板使用:
import requests from django.conf import settings class Comment_List_TV(ListView): template_name = 'account/user_comment_list_tv.html' def get_queryset(self): if self.request.user.is_authenticated: return Comment_tv.objects.filter(user=self.request.user).select_related('tv') return Comment_tv.objects.none() def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) comments = context['object_list'] # 收集所有需要查询的TV ID tv_ids = [comment.tv.id for comment in comments] tmdb_data_map = {} for tv_id in tv_ids: try: response = requests.get( f"https://api.themoviedb.org/3/tv/{tv_id}?api_key={settings.TMDB_API_KEY}&language=en-US" ) response.raise_for_status() tmdb_data_map[tv_id] = response.json() except requests.exceptions.RequestException: # 处理请求异常,比如网络错误、API限流等 tmdb_data_map[tv_id] = None context['tmdb_data_map'] = tmdb_data_map return context
方式二:模型方法获取(适合单条评论场景)
在TV模型中添加方法获取TMDB数据,并加入缓存避免重复请求:
from django.core.cache import cache import requests from django.conf import settings class TV(models.Model): # 原有字段和方法... def get_tmdb_data(self): cache_key = f'tv_tmdb_info_{self.id}' data = cache.get(cache_key) if not data: try: response = requests.get( f"https://api.themoviedb.org/3/tv/{self.id}?api_key={settings.TMDB_API_KEY}&language=en-US" ) response.raise_for_status() data = response.json() # 缓存1小时,可根据需求调整时长 cache.set(cache_key, data, 3600) except requests.exceptions.RequestException: data = None return data
3. 模板中展示评论与TV数据
对应方式一的模板代码
{% for comment in object_list %} <div class="comment-card"> <div class="comment-content"> <p>评分:{{ comment.stars }}</p> <p>评论:{{ comment.comment }}</p> <p>评论时间:{{ comment.created_at|date:"Y-m-d H:i" }}</p> </div> <div class="tv-info"> {% with tv_info=tmdb_data_map[comment.tv.id] %} {% if tv_info %} <h3>{{ tv_info.name }}</h3> <p>{{ tv_info.overview|truncatechars:200 }}</p> {% if tv_info.poster_path %} <img src="https://image.tmdb.org/t/p/w200{{ tv_info.poster_path }}" alt="{{ tv_info.name }}"> {% endif %} {% else %} <p>无法获取该TV的详细信息</p> {% endif %} {% endwith %} </div> </div> {% empty %} <p>暂无评论</p> {% endfor %}
对应方式二的模板代码
{% for comment in object_list %} <div class="comment-card"> <div class="comment-content"> <p>评分:{{ comment.stars }}</p> <p>评论:{{ comment.comment }}</p> <p>评论时间:{{ comment.created_at|date:"Y-m-d H:i" }}</p> </div> <div class="tv-info"> {% with tv_info=comment.tv.get_tmdb_data %} {% if tv_info %} <h3>{{ tv_info.name }}</h3> <p>{{ tv_info.overview|truncatechars:200 }}</p> {% if tv_info.poster_path %} <img src="https://image.tmdb.org/t/p/w200{{ tv_info.poster_path }}" alt="{{ tv_info.name }}"> {% endif %} {% else %} <p>无法获取该TV的详细信息</p> {% endif %} {% endwith %} </div> </div> {% empty %} <p>暂无评论</p> {% endfor %}
额外优化建议
- 对TMDB API请求添加缓存,避免重复调用,降低API限流风险
- 增加异常处理逻辑,比如记录API请求错误日志
- 如果评论数量较多,添加分页功能提升页面加载性能
内容的提问来源于stack exchange,提问作者user19553515
相关产品推荐
相关产品推荐

