Django一对多关联查询:如何展示帖子及关联首张图片?
Django实现首页展示帖子及关联首张图片的解决方案
错误原因梳理
select_related('Media')报错是因为select_related仅支持正向的多对一/一对一关系查询,你的Post和Media是一对多的反向关联(Media关联Post),Post模型里并没有名为Media的外键字段,所以不能这么用。- 之前用
Media.objects.all()会导致同一个帖子被重复展示(有几张图就显示几次),完全不符合“每个帖子只显示一次+首张图片”的需求。
方案一:用annotate直接获取每个帖子的首张图片(推荐,性能更优)
通过数据库层面的子查询,直接给每个Post对象添加first_photo字段,存储对应首张图片的信息,无需在模板中额外处理关联数据。
修改视图代码
from django.db.models import Subquery, OuterRef from .models import Post, Media def index(request): # 子查询:获取每个Post对应的第一个Media(按id排序)的photo字段 first_media = Media.objects.filter(post=OuterRef('pk')).order_by('id')[:1] # 给Post对象添加first_photo注解 posts = Post.objects.annotate( first_photo=Subquery(first_media.values('photo')) ).all() paginator = Paginator(posts, 2) page_number = request.GET.get('page') page_obj = paginator.get_page(page_number) return render(request, 'aggregator/index.html', {'page': page_obj})
修改模板代码
现在循环的是Post对象,直接调用注解的first_photo字段即可:
{% for post in page %} <div class="col"> <div> {% if post.first_photo %} <img src="{{ post.first_photo.url }}" alt="{{ post.title }}"> {% else %} <p>无图片</p> {% endif %} </div> <div> <h4><a href="{% url 'show_post' post_id=post.id %}">{{ post.title|upper }}</a></h4> </div> <div> <h5>{{ post.price }}$</h5> </div> <div> <h6>{{ post.city }}</h6> </div> </div> {% endfor %}
方案二:用prefetch_related预加载首张图片(适合需访问多个图片的场景)
如果后续需要展示帖子的多张图片,可通过Prefetch指定预加载每个Post的首个Media对象,减少数据库查询次数。
修改视图代码
from django.db.models import Prefetch from .models import Post, Media def index(request): # 预加载每个Post的首个Media(按id排序),并存储到first_media属性 posts = Post.objects.prefetch_related( Prefetch( 'media_set', # Post反向关联Media的默认名称(模型名小写+_set) queryset=Media.objects.order_by('id')[:1], to_attr='first_media' ) ).all() paginator = Paginator(posts, 2) page_number = request.GET.get('page') page_obj = paginator.get_page(page_number) return render(request, 'aggregator/index.html', {'page': page_obj})
修改模板代码
从预加载的first_media列表中取第一个元素:
{% for post in page %} <div class="col"> <div> {% if post.first_media %} <img src="{{ post.first_media.0.photo.url }}" alt="{{ post.title }}"> {% else %} <p>无图片</p> {% endif %} </div> <div> <h4><a href="{% url 'show_post' post_id=post.id %}">{{ post.title|upper }}</a></h4> </div> <div> <h5>{{ post.price }}$</h5> </div> <div> <h6>{{ post.city }}</h6> </div> </div> {% endfor %}
内容的提问来源于stack exchange,提问作者sergeusprecious
相关产品推荐
相关产品推荐

