Django:比较两个QuerySet生成带好友标记的用户列表(避免模板嵌套循环)
解决Django中高效标记用户好友状态的方案
嘿,我完全懂你的痛点——在模板里嵌套循环比对两个QuerySet不仅写起来麻烦,还特别影响性能,尤其是用户多的时候。给你两个干净利落的解决方案,都能避免在模板里做这种低效操作:
方案一:数据库层面用annotate + Exists子查询(最优解)
这是性能最好的方式,直接让数据库帮我们完成好友关系的判断,一次查询搞定所有数据。
首先,假设你的Friends模型是类似这样的(如果结构不同,你只需要调整下面的过滤条件就行):
class Friends(models.Model): user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='sent_friendships') friend = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='received_friendships') status = models.CharField(max_length=20, default='pending') # 比如有pending/accepted状态
在你的视图函数里,用annotate给每个UserProfile对象新增一个is_friend字段:
from django.db.models import Exists, OuterRef def user_list_view(request): # 获取当前用户的UserProfile current_profile = request.user.userprofile # 标注每个用户是否是当前用户的已通过好友 user_profiles = UserProfile.objects.annotate( is_friend=Exists( # 匹配当前用户发起的、对方已接受的好友关系 Friends.objects.filter( user=current_profile, friend=OuterRef('pk'), status='accepted' ) | # 匹配对方发起的、当前用户已接受的好友关系 Friends.objects.filter( user=OuterRef('pk'), friend=current_profile, status='accepted' ) ) ).exclude(pk=current_profile.pk) # 排除当前用户自己 return render(request, 'user_list.html', {'user_profiles': user_profiles})
然后在模板里直接用这个字段就行,完全不用嵌套循环:
{% for profile in user_profiles %} <div class="user-card"> <h3>{{ profile.user.username }}</h3> {% if profile.is_friend %} <span class="tag tag-success">已加好友</span> {% else %} <a href="{% url 'send_friend_request' profile.pk %}" class="btn btn-primary">添加好友</a> {% endif %} </div> {% endfor %}
这个方案的优势:数据库层面一次性完成所有计算,没有Python层面的循环比对,性能拉满,模板代码也非常简洁。
方案二:视图层面预处理好友ID列表
如果暂时不想用子查询,也可以在视图里先把当前用户的所有好友ID提取出来,再给每个UserProfile手动添加is_friend属性:
def user_list_view(request): current_profile = request.user.userprofile # 获取所有已通过好友的ID列表 # 这里根据你的Friends模型结构调整查询 friend_ids = set( # 当前用户发起的已通过好友 Friends.objects.filter(user=current_profile, status='accepted').values_list('friend__pk', flat=True) | # 对方发起的已通过好友 Friends.objects.filter(friend=current_profile, status='accepted').values_list('user__pk', flat=True) ) # 获取所有用户(排除自己) user_profiles = UserProfile.objects.exclude(pk=current_profile.pk) # 给每个用户添加is_friend属性 for profile in user_profiles: profile.is_friend = profile.pk in friend_ids return render(request, 'user_list.html', {'user_profiles': user_profiles})
模板里的用法和方案一完全一样,这个方案比模板嵌套循环好太多,因为逻辑都在视图里处理,模板只负责展示,不过性能比方案一略逊一筹,适合对数据库查询不太熟悉的情况。
注意事项
- 一定要根据你实际的
Friends模型结构调整查询条件,比如如果你的好友关系是单向的、或者没有状态字段,就去掉对应的过滤条件。 - 记得排除当前用户自己,避免在列表里显示自己的资料。
内容的提问来源于stack exchange,提问作者Marco Polo
相关产品推荐
相关产品推荐

