You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 12:06:05