Django模型关联问题:如何在计分表表单中录入单场球员数据
问题分析
你当前的模型设计无法实现单场球员统计数据关联的核心需求,原因是:
Score_sheet模型中的goals、freekicks等字段是球队整体数据,无法对应到单个球员players多对多字段仅能关联球员与赛事,但无法存储每个球员在这场比赛的个性化统计数据
解决方案
需要新增一个球员单场统计模型,作为Score_sheet与Player的关联中间表,专门存储单场中每个球员的进球、红黄牌等数据;同时调整Score_sheet的关联逻辑,结合Django的inlineformset实现表单中同时录入球队与球员数据。
修改后的模型代码
from django.db import models from django.contrib.auth.models import User # 假设Category和Season模型已定义 class Category(models.Model): name = models.CharField(max_length=40) # 其他字段... class Season(models.Model): name = models.CharField(max_length=40) # 其他字段... Roles = [ ('default', 'Default'), # 其他角色选项... ] class Player(models.Model): # 修正字段命名为Django推荐的snake_case user = models.OneToOneField(User, on_delete=models.SET_NULL, null=True, related_name="playeruser") player_name = models.CharField(max_length=40) player_surname = models.CharField(max_length=40) player_id = models.CharField(max_length=40) player_email = models.CharField(max_length=40) player_contact = models.CharField(max_length=40) category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="pcategory") player_preferred = models.CharField(choices=Roles, default="default", max_length=16) def __str__(self): return f"{self.player_name} {self.player_surname}" # 新增球员单场统计模型 class PlayerGameStats(models.Model): score_sheet = models.ForeignKey('Score_sheet', on_delete=models.CASCADE, related_name='player_stats') player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name='game_stats') goals = models.IntegerField(default=0) freekicks = models.IntegerField(default=0) red_cards = models.IntegerField(default=0) yellow_cards = models.IntegerField(default=0) def __str__(self): return f"{self.player} - {self.score_sheet}" class Score_sheet(models.Model): season = models.ForeignKey(Season, on_delete=models.CASCADE, related_name="season") category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="sscategory") # 多对多关联改为通过PlayerGameStats中间表 players = models.ManyToManyField(Player, through='PlayerGameStats', related_name='score_sheets') captain = models.ForeignKey(Player, on_delete=models.CASCADE, related_name="captain") date = models.DateField() venue = models.CharField(max_length=40, null=True, blank=True) description = models.TextField(null=True, blank=True) # 若需要球队总数据,可保留以下字段,或通过PlayerGameStats自动计算 total_goals = models.IntegerField(null=True, blank=True) total_freekicks = models.IntegerField(null=True, blank=True) total_redcards = models.IntegerField(null=True, blank=True) total_yellowcards = models.IntegerField(null=True, blank=True) def __str__(self): return f"{self.season} - {self.venue}"
表单实现(关联球员统计)
使用Django的inlineformset_factory,在计分表表单中嵌入球员单场统计的表单集,实现一次提交同时录入球队与球员数据:
# views.py from django.shortcuts import render, redirect from .models import Score_sheet, PlayerGameStats from .forms import ScoreSheetForm from django.forms import inlineformset_factory # 创建球员统计表单集 PlayerStatsFormSet = inlineformset_factory( Score_sheet, PlayerGameStats, fields=('player', 'goals', 'freekicks', 'red_cards', 'yellow_cards'), extra=10, # 默认显示10个球员统计输入框,可根据需求调整 can_delete=True ) def create_score_sheet(request): if request.method == 'POST': form = ScoreSheetForm(request.POST) formset = PlayerStatsFormSet(request.POST, instance=form.instance) if form.is_valid() and formset.is_valid(): score_sheet = form.save() formset.save() # 可选:自动计算球队总数据 score_sheet.total_goals = sum(stat.goals for stat in score_sheet.player_stats.all()) score_sheet.save() return redirect('score_sheet_list') else: form = ScoreSheetForm() formset = PlayerStatsFormSet(instance=form.instance) return render(request, 'create_score_sheet.html', {'form': form, 'formset': formset})
模板示例(create_score_sheet.html)
在模板中同时渲染计分表表单和球员统计表单集:
<form method="post"> {% csrf_token %} {{ form.as_p }} <h3>球员单场统计</h3> {{ formset.management_form }} {% for form in formset %} {{ form.as_table }} <hr> {% endfor %} <button type="submit">保存计分表</button> </form>
关键说明
- 中间表作用:
PlayerGameStats模型作为关联层,实现了"一场比赛-多个球员-每个球员的单场数据"的关联逻辑 - 表单集优势:
inlineformset允许在同一个页面处理主模型(Score_sheet)和关联模型(PlayerGameStats)的表单,符合业务流程 - 字段规范:将原模型中的大写字段名改为
snake_case,符合Python/Django的编码惯例,提升代码可读性
内容的提问来源于stack exchange,提问作者Eesa Kamaldien
相关产品推荐
相关产品推荐

