Django中训练(Workout)与动作(Exercise)Many to Many关联模型设计问题
Django 健身训练追踪系统模型设计方案
核心设计逻辑
你需要的是带自定义参数的多对多关联结构:预设动作库可全局复用,单次训练和动作的关联数据需要单独存储每组的组数、次数、负重参数,因此需要用Django的through参数定义多对多中间表实现。
完整模型代码
from django.db import models from django.contrib.auth import get_user_model User = get_user_model() # 预设训练动作库 class Exercise(models.Model): name = models.CharField(max_length=100, unique=True, verbose_name="动作名称") # 如果需要更规范的标签管理,可单独建立Tag模型做多对多关联,此处用JSONField简化实现 tags = models.JSONField(default=list, verbose_name="动作标签", help_text="如:['胸部', '杠铃', '复合动作']") class Meta: verbose_name = "训练动作" verbose_name_plural = "训练动作库" def __str__(self): return self.name # 单次训练记录 class Workout(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="训练者") name = models.CharField(max_length=100, verbose_name="训练名称", help_text="如:周一胸部训练") trainer_weight = models.FloatField(verbose_name="训练者体重(kg)", null=True, blank=True) training_date = models.DateField(verbose_name="训练日期") # 多对多关联动作库,通过中间表存储每组参数 exercises = models.ManyToManyField(Exercise, through="WorkoutExerciseEntry", verbose_name="训练动作列表") class Meta: verbose_name = "训练记录" verbose_name_plural = "训练记录" def __str__(self): return f"{self.user.username} - {self.name} - {self.training_date}" # 多对多中间表:存储单次训练中某个动作的单组参数 class WorkoutExerciseEntry(models.Model): workout = models.ForeignKey(Workout, on_delete=models.CASCADE, verbose_name="所属训练") exercise = models.ForeignKey(Exercise, on_delete=models.CASCADE, verbose_name="训练动作") set_number = models.PositiveSmallIntegerField(verbose_name="组号", help_text="第1组、第2组等") reps = models.PositiveSmallIntegerField(verbose_name="次数") weight = models.FloatField(verbose_name="负重(kg)", default=0, help_text="自重训练可填0") class Meta: verbose_name = "训练动作组详情" verbose_name_plural = "训练动作组详情" # 唯一约束:避免同一个训练的同一个动作出现重复组号 constraints = [ models.UniqueConstraint(fields=['workout', 'exercise', 'set_number'], name='unique_workout_exercise_set') ] def __str__(self): return f"{self.workout.name} - {self.exercise.name} 第{self.set_number}组:{self.reps}次 {self.weight}kg"
常用操作示例
1. 新增训练并关联动作组
# 先获取预设动作 bench_press = Exercise.objects.get(name="杠铃卧推") incline_dumbbell = Exercise.objects.get(name="上斜哑铃卧推") # 创建训练记录 new_workout = Workout.objects.create( user=request.user, name="周一胸部训练", trainer_weight=75.5, training_date="2024-05-20" ) # 给训练添加动作组 WorkoutExerciseEntry.objects.create(workout=new_workout, exercise=bench_press, set_number=1, reps=12, weight=60) WorkoutExerciseEntry.objects.create(workout=new_workout, exercise=bench_press, set_number=2, reps=10, weight=70) WorkoutExerciseEntry.objects.create(workout=new_workout, exercise=bench_press, set_number=3, reps=8, weight=80) WorkoutExerciseEntry.objects.create(workout=new_workout, exercise=incline_dumbbell, set_number=1, reps=15, weight=20)
2. 查询单次训练的所有动作数据
workout = Workout.objects.get(id=1) # 获取所有动作组详情 exercise_entries = workout.workoutexerciseentry_set.all().order_by('exercise', 'set_number') for entry in exercise_entries: print(f"{entry.exercise.name} 第{entry.set_number}组:{entry.reps}次 {entry.weight}kg")
可选优化点
- 单独创建
Tag模型,和Exercise做多对多关联,方便标签的统一管理和筛选 - 给
WorkoutExerciseEntry添加rpe(自觉用力程度)、notes(组备注)字段,满足更精细的记录需求 - 给负重字段添加
unit选项,支持kg、lb两种单位
内容的提问来源于stack exchange,提问作者Mika
相关产品推荐
相关产品推荐

