基于Django开发赛事竞猜Web应用的用户竞猜逻辑开发问题求助
赛事竞猜功能实现方案
现有数据表设计评估
- 你当前的
Bet表核心字段逻辑没有问题,覆盖了投注关联的三个核心对象:投注用户、关联赛事、用户选择,只需要补两处约束就能避免业务bug:- 添加
(user, game_id)联合唯一约束,从数据库层面阻止同一用户对同一场赛事重复提交投注 - 不需要在
Bet表冗余存储比赛周字段,比赛周属性可以通过外键关联的Game表查询获取,避免数据不一致
- 添加
- 建议给存储赛事的
Game表补充三个必要字段,支撑后续竞猜截止、计分逻辑:week:整数类型,标记赛事所属比赛周is_locked:布尔类型,默认False,赛事开场/截止投注后置为True,禁止用户修改投注actual_result:和Bet.user_pick字段格式完全一致,存储赛事最终结果,方便后续直接比对算分
Formset实现竞猜提交的正确逻辑
你之前卡壳的核心原因是没有在后端层面绑定表单对应的赛事、自动注入当前登录用户,把不该暴露给前端的字段放到了表单里,按下面的步骤实现即可:
- 定义单场投注的ModelForm,只暴露用户选择字段,用户、赛事关联字段全部由后端赋值,禁止前端传参篡改:
from django import forms from .models import Bet class SingleBetForm(forms.ModelForm): class Meta: model = Bet fields = ["user_pick"] widgets = { # 根据你的竞猜选项调整,比如胜/平/负对应三个单选 "user_pick": forms.RadioSelect } - 视图层动态生成对应比赛周的formset,提前查询当周所有可投注赛事,给每个表单绑定对应赛事,提交时自动关联当前登录用户:
from django.forms import modelformset_factory from django.shortcuts import render, redirect from .models import Game, Bet def weekly_betting_view(request, week_number): # 查询当前周所有未截止投注的赛事 current_week_games = Game.objects.filter(week=week_number, is_locked=False) game_count = current_week_games.count() # 生成formset,表单数和当周可投注赛事数严格一致 BetFormSet = modelformset_factory( Bet, form=SingleBetForm, extra=game_count, max_num=game_count ) if request.method == "POST": formset = BetFormSet(request.POST) if formset.is_valid(): # 先不提交到数据库,补全后端自动赋值的字段 bet_instances = formset.save(commit=False) for bet, game in zip(bet_instances, current_week_games): bet.user = request.user bet.game = game bet.save() return redirect("bet_submit_success") else: # GET请求初始化formset,给每个表单绑定对应赛事id formset = BetFormSet( queryset=Bet.objects.none(), initial=[{"game": game} for game in current_week_games] ) # 把表单和对应赛事信息打包,方便模板渲染对阵信息 form_game_pairs = list(zip(formset.forms, current_week_games)) return render(request, "weekly_bet.html", { "form_game_pairs": form_game_pairs, "formset": formset, "current_week": week_number })模板渲染时必须在表单最开头加上
{{ formset.management_form }},否则formset会直接校验失败。遍历form_game_pairs时,每一项先输出对阵双方信息(比如{{ pair.1.home_team }} VS {{ pair.1.away_team }}),再输出pair.0的表单字段即可。
计分逻辑实现
建议在管理员录入完单周所有赛事的赛果后,触发结算逻辑,不要实时计算,避免重复计分:
- 单场周积分统计:遍历当周所有已出结果的赛事,查询对应赛事的所有投注记录,比对
user_pick和赛事的actual_result,值一致的用户累计1个周积分 - 周榜总积分结算:统计当周所有用户的周积分,取积分最高的用户(如果多人同分并列第一,可根据业务规则决定是平分25积分还是都拿25积分),给对应用户的总积分榜累计值加25
- 结算逻辑一定要加数据库事务,同时给周次加结算标记,避免重复触发导致用户积分重复累加。
内容的提问来源于stack exchange,提问作者Martin Bedak
相关产品推荐
相关产品推荐

