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

Django基于模板模型批量生成关联实例的方案咨询

架构优化建议
  • 现有三层模型拆分逻辑完全合理:Exercise作为基础动作库(支持后续用户自定义动作)、ExerciseTemplate+WorkoutTemplate作为可复用的训练计划模板、ExerciseInstance+WorkoutInstance作为每次实际训练的记录,完全匹配健身追踪的业务流程,后续要加训练数据统计、模板分享、动作PR记录都不用重构,没必要简化层级。
  • 修正现有模型的关联错误:当前ExerciseTemplate和ExerciseInstance里的workout外键指向不存在的Workout模型,需要分别改成指向WorkoutTemplate和WorkoutInstance,否则关联查询会直接报错。
  • 补全必要业务字段:ExerciseInstance增加set_number字段区分同个动作的不同组;ExerciseTemplate增加推荐次数、推荐重量字段,生成实例时可以预填默认值;WorkoutInstance增加训练开始/结束时间、备注字段,让实例有实际业务意义。
  • 修复多对多字段配置:原WorkoutTemplate里的exercises多对多字段因为中间表外键名不是Django默认值,要么补充through_fields参数,要么直接删掉,通过反向关联查询模板下的动作更直观。
代码实现

修正后的核心模型定义

from django.db import models

class Exercise(models.Model):
    name = models.CharField(max_length=128)
    description = models.TextField(null=True, blank=True)

    def __str__(self):
        return self.name

class ExerciseTemplate(models.Model):
    # 修正外键:关联所属训练模板
    workout_template = models.ForeignKey(
        'WorkoutTemplate',
        on_delete=models.CASCADE,
        related_name="exercise_templates"
    )
    exercise = models.ForeignKey(
        Exercise, 
        on_delete=models.SET_NULL, 
        null=True, 
        blank=True
    )
    recommended_sets = models.PositiveIntegerField(default=3)
    recommended_reps = models.PositiveIntegerField(default=12, null=True, blank=True)
    recommended_weight = models.PositiveIntegerField(null=True, blank=True)

    def __str__(self):
        return f"{self.exercise.name}(模板)"

class ExerciseInstance(models.Model):
    exercise_template = models.ForeignKey(
        ExerciseTemplate, 
        on_delete=models.SET_NULL, 
        null=True, 
        blank=True
    )
    # 修正外键:关联所属实际训练记录
    workout_instance = models.ForeignKey(
        'WorkoutInstance',
        on_delete=models.CASCADE,
        related_name="exercise_instances"
    )
    # 新增组数字段
    set_number = models.PositiveIntegerField()
    weight = models.PositiveIntegerField(null=True, blank=True)
    reps = models.PositiveIntegerField(null=True, blank=True)
    is_completed = models.BooleanField(default=False)

class WorkoutTemplate(models.Model):
    name = models.CharField(max_length=128)
    description = models.TextField(null=True, blank=True)
    date = models.DateTimeField(null=True, blank=True)
    # 原多对多字段可保留,需补充through_fields,或直接删除用反向关联查询
    # exercises = models.ManyToManyField(
    #     Exercise, 
    #     through=ExerciseTemplate,
    #     through_fields=('workout_template', 'exercise')
    # )

    def __str__(self):
        return self.name

    # 新增:从模板生成训练实例的自定义方法
    def create_workout_instance(self, user_note=None):
        # 先创建训练记录主条目
        workout_instance = WorkoutInstance.objects.create(
            workout_template=self,
            note=user_note
        )
        # 准备批量生成的动作实例列表
        instance_list = []
        # 遍历模板下所有动作,按推荐组数生成对应实例
        for ex_template in self.exercise_templates.all():
            for set_idx in range(1, ex_template.recommended_sets + 1):
                instance_list.append(
                    ExerciseInstance(
                        exercise_template=ex_template,
                        workout_instance=workout_instance,
                        set_number=set_idx,
                        # 预填模板推荐值,用户训练时可直接修改
                        reps=ex_template.recommended_reps,
                        weight=ex_template.recommended_weight
                    )
                )
        # 批量写入数据库,减少IO次数
        ExerciseInstance.objects.bulk_create(instance_list)
        return workout_instance

class WorkoutInstance(models.Model):
    workout_template = models.ForeignKey(
        WorkoutTemplate,
        on_delete=models.SET_NULL, 
        null=True, 
        blank=True
    )
    # 新增训练记录基础字段
    start_time = models.DateTimeField(auto_now_add=True)
    end_time = models.DateTimeField(null=True, blank=True)
    note = models.TextField(null=True, blank=True)

调用方式

  • 从动作库创建训练模板时,直接新建ExerciseTemplate实例绑定对应的Exercise和WorkoutTemplate即可,后续支持用户自定义Exercise时逻辑完全不用改。
  • 从模板生成实际训练记录只需要一行调用:
# 例:获取id为1的练腿模板
leg_day_template = WorkoutTemplate.objects.get(id=1)
# 一键生成预填好所有动作组的训练记录
today_workout = leg_day_template.create_workout_instance(user_note="今天减载")
  • 生成后通过today_workout.exercise_instances.all()即可拿到所有预生成的动作组,用户修改重量、次数、标记完成都直接操作对应的ExerciseInstance,不会修改原模板的配置。

内容的提问来源于stack exchange,提问作者lcardona89

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 06:48:24