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

基于Django开发赛事竞猜Web应用的用户竞猜逻辑开发问题求助

赛事竞猜功能实现方案

现有数据表设计评估

  • 你当前的Bet表核心字段逻辑没有问题,覆盖了投注关联的三个核心对象:投注用户、关联赛事、用户选择,只需要补两处约束就能避免业务bug:
    • 添加(user, game_id)联合唯一约束,从数据库层面阻止同一用户对同一场赛事重复提交投注
    • 不需要在Bet表冗余存储比赛周字段,比赛周属性可以通过外键关联的Game表查询获取,避免数据不一致
  • 建议给存储赛事的Game表补充三个必要字段,支撑后续竞猜截止、计分逻辑:
    • week:整数类型,标记赛事所属比赛周
    • is_locked:布尔类型,默认False,赛事开场/截止投注后置为True,禁止用户修改投注
    • actual_result:和Bet.user_pick字段格式完全一致,存储赛事最终结果,方便后续直接比对算分

Formset实现竞猜提交的正确逻辑

你之前卡壳的核心原因是没有在后端层面绑定表单对应的赛事、自动注入当前登录用户,把不该暴露给前端的字段放到了表单里,按下面的步骤实现即可:

  1. 定义单场投注的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
            }
    
  2. 视图层动态生成对应比赛周的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 16:21:32