Django中对多对多字段进行分组的实现方法
解决Django按类别分组展示用户关联练习的问题
首先先修正你模型里的一个明显错误:CategoryExercisesThrough类里的category字段关联的是ExerciseCollections,但你定义的类别模型是ExerciseCategory,这会导致数据库外键错误,改成下面这样:
class CategoryExercisesThrough(models.Model): #Through table for the many-to-many relationship between ExerciseCategory and Exercise category= models.ForeignKey(ExerciseCategory, on_delete=models.CASCADE) exercise = models.ForeignKey(Exercise, on_delete=models.CASCADE)
接下来是核心需求:获取指定用户的所有关联练习,并按类别分组整理成{类别名称: [练习列表]}的结构。
方法一:视图中手动整理分组数据
在视图函数里先查询目标用户的所有UserExercises,再遍历每个练习及其关联的类别,将练习归类到对应类别的列表中:
from django.shortcuts import render from .models import UserExercises def user_exercises_by_category(request, user_id): # 获取指定用户的所有关联练习,预加载练习对象避免N+1查询 user_exercises = UserExercises.objects.filter(user_id=user_id).select_related('exercise') # 初始化分组字典 category_exercises = {} for ue in user_exercises: exercise = ue.exercise # 遍历当前练习关联的所有类别 for category in exercise.category.all(): # 若类别不在字典中,先创建空列表 if category.name not in category_exercises: category_exercises[category.name] = [] # 避免同一练习重复加入同一类别列表 if exercise not in category_exercises[category.name]: category_exercises[category.name].append(exercise) return render(request, 'your_template.html', {'category_exercises': category_exercises})
方法二:用ORM聚合优化查询(数据量大时更高效)
直接查询用户关联的所有练习,预加载关联类别,再做分组整理:
from django.shortcuts import render from .models import Exercise def user_exercises_by_category(request, user_id): # 查询用户关联的所有练习,预加载类别减少数据库请求 exercises = Exercise.objects.filter(assignee__id=user_id).prefetch_related('category').distinct() category_exercises = {} for exercise in exercises: for category in exercise.category.all(): if category.name not in category_exercises: category_exercises[category.name] = [] category_exercises[category.name].append(exercise) return render(request, 'your_template.html', {'category_exercises': category_exercises})
模板中渲染分组数据
在模板里通过循环遍历分组后的字典,按类别展示对应的练习:
{% for category_name, exercises in category_exercises.items %} <h3>{{ category_name }}</h3> <ul> {% for exercise in exercises %} <li>{{ exercise.name }}</li> <!-- 替换成Exercise实际需要展示的字段 --> {% endfor %} </ul> {% empty %} <p>该用户暂无关联练习</p> {% endfor %}
额外提示
- 若需要类别按名称排序,可在视图里对字典项排序后再传给模板:
sorted_categories = sorted(category_exercises.items(), key=lambda x: x[0]),模板遍历sorted_categories即可 - 如果练习可能重复关联同一用户,记得在查询时加上
.distinct()去重
内容的提问来源于stack exchange,提问作者Alessio Giardina
相关产品推荐
相关产品推荐

