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

在Django中使用JavaScript更新Score模型字段问题求助

Django数学游戏分数无法同步到数据库问题排查与修复

问题描述

用Django开发数学游戏,通过JavaScript实现无页面刷新运行。游戏结束后需更新Score字段,但仅前端显示分数更新,数据库中Score值始终为0,尝试Ajax同步未成功。

相关代码

前端游戏逻辑代码(JavaScript)

let number1;
let number2;

operator = document.getElementById('operator').innerHTML;
max = Number(document.getElementById('max').innerHTML);

if (operator == '+') {
    number1 = Math.floor((Math.random() * max) + 1);
    number2 = Math.floor((Math.random() * max) + 1);
} else if (operator == '-') {
    number1 = Math.floor((Math.random() * max) + 1);
    number2 = Math.floor((Math.random() * max) + 1);
    if (number2 > number1) {
            // 原交换逻辑错误,未实际交换两个数
            number2 = number1
            number1 = number2
    }
} else if (operator == '*') {
    number1 = Math.floor((Math.random() * max) + 1);
    number2 = Math.floor((Math.random() * max) + 1);
} else {
    number2 = Math.floor((Math.random() * max) + 1);
    numberx = Math.floor((Math.random() * max) + 1);
    number1 = number2 * numberx
}

document.getElementById('number1').innerHTML=number1;
document.getElementById('number2').innerHTML=number2;
document.getElementById('tbInput').focus();

window.addEventListener("keydown", function(e) {
    if (e.key === 'Enter' || e.keycode === 13) {
        if (operator == '+') {
            answer = number1 + number2;
        } else if (operator == '-') {
            answer = number1 - number2;
        } else if (operator == '*') {
            answer = number1 * number2;
        } else {
            answer = number1 / number2;
        }

        const checkAnswer = document.getElementById('tbInput');
        const value = checkAnswer.value;
        let playerScore = + this.document.getElementById('playerScore').innerHTML;
        
        if (value == answer) {
              playerScore+=1;
              document.getElementById('playerScore').innerHTML=playerScore;
              // 原代码未调用Ajax同步函数
        } else {
              alert('回答错误,正确答案是 ' + answer);
        }

        document.querySelector('input[type=text]').value = "";               
        document.getElementById('number1').innerHTML = "";
        document.getElementById('number2').innerHTML = ""; 
        if (operator == '+') {
            number1 = Math.floor((Math.random() * max) + 1);
            number2 = Math.floor((Math.random() * max) + 1);
        } else if (operator == '-') {
            number1 = Math.floor((Math.random() * max) + 1);
            number2 = Math.floor((Math.random() * max) + 1);
            if (number2 > number1) {
                    number2 = number1
                    number1 = number2
            }
        } else if (operator == '*') {
            number1 = Math.floor((Math.random() * max) + 1);
            number2 = Math.floor((Math.random() * max) + 1);
        } else {
            number2 = Math.floor((Math.random() * max) + 1);
            numberx = Math.floor((Math.random() * max) + 1);
            number1 = number2 * numberx
        }
        document.getElementById('number1').innerHTML = number1; 
        document.getElementById('number2').innerHTML = number2;
        document.getElementById('tbInput').focus();
    }
});

Ajax同步函数(JavaScript)

function register(score) {
    const csrfInput =  document.querySelector("input[name='csrfmiddlewaretoken']");
    const csrfToken = csrfInput.value;
    // 参数score冗余,内部重新获取
    const score = Number(document.getElementById('playerScore').innerHTML);
    const data = {
        'score': score,
    }
    // ajaxURL未定义,需确保能访问到正确的后端路由
    fetch(ajaxURL, {
        method: 'POST',
        headers: {
        'Content-Type': 'application/json',
        'X-CSRFToken': csrfToken
        },
        body: JSON.stringify(data),
    })
    // 未处理响应与错误捕获
}

Django视图代码(views.py)

import json
from django.http import JsonResponse
from django.views.generic import CreateView, DetailView, ListView, UpdateView
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import Mathgame
from .forms import MathgameForm, MathgamePlayForm

class MathgameCreateView(CreateView):
    model = Mathgame
    form_class = MathgameForm

    def form_valid(self, form):
        form.instance.user = self.request.user
        return super().form_valid(form)

class MathgameDetailView(DetailView):
    model = Mathgame

class MathgameListView(LoginRequiredMixin, ListView):
    model = Mathgame
    paginate_by = 10

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        order_fields, order_key, direction = self.get_order_settings()

        context['order'] = order_key
        context['direction'] = direction
        
        # 排除最后一个默认排序键
        context['order_fields'] = list(order_fields.keys())[:-1]

        return context
    
    def get_ordering(self):
        order_fields = self.get_order_fields()
        default_order_key = order_fields['default_key']
        order_key = self.request.GET.get('order', default_order_key)
        direction = self.request.GET.get('direction', 'desc')
        
        # 排序键无效时使用默认值
        if order_key not in order_fields:
            order_key = default_order_key
        
        ordering = order_fields[order_key]

        # 非升序则使用降序
        if direction != 'asc':
            ordering = '-' + ordering

        return ordering

    def get_order_settings(self):
        order_fields = self.get_order_fields()
        default_order_key = order_fields['default_key']
        order_key = self.request.GET.get('order', default_order_key)
        direction = self.request.GET.get('direction', 'desc')
        
        # 排序键无效时使用默认值
        if order_key not in order_fields:
            order_key = default_order_key

        return (order_fields, order_key, direction)

    
    def get_order_fields(self):
        # 友好名称与字段名的映射
        return {
            'updated': 'updated',
            'game': 'operation',
            'max': 'max_number',
            'creator': 'user__username',
            'score': 'score',
            'default_key': 'score'
        }
    
    def get_queryset(self):
        ordering = self.get_ordering()
        qs = Mathgame.objects.all()

        if 'username' in self.kwargs:
            username = self.kwargs['username']
            qs = qs.filter(user__username=username)

        return qs.order_by(ordering)

class MathgameUpdateView(UpdateView):
    model = Mathgame
    form_class = MathgamePlayForm

def score(request, slug):
    user = request.user
    mathgame = Mathgame.objects.get(slug=slug)
    data = json.loads(request.body)

    score = data['score']

    # 错误:新建Mathgame实例而非更新现有游戏的Score字段
    mathgame_score = Mathgame(user=user, mathgame=mathgame, score=score)
    mathgame_score.save()

    response = {
        'score': score
    }
    return JsonResponse(response)

问题排查与修复方案

1. 前端未调用Ajax同步函数

在答对题目更新前端分数后,立即调用register()函数触发同步:

if (value == answer) {
    playerScore+=1;
    document.getElementById('playerScore').innerHTML=playerScore;
    register(); // 新增:调用Ajax同步分数
}

2. 修复Ajax函数缺陷

  • 移除冗余参数,确保ajaxURL正确获取(可在Django模板中通过反向路由传递)
  • 添加响应处理与错误捕获:
function register() {
    const csrfInput = document.querySelector("input[name='csrfmiddlewaretoken']");
    const csrfToken = csrfInput.value;
    const score = Number(document.getElementById('playerScore').innerHTML);
    const data = { 'score': score };
    
    // 确保ajaxURL在模板中定义,例如:
    // <script>const ajaxURL = "{% url 'score' mathgame.slug %}";</script>
    fetch(ajaxURL, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRFToken': csrfToken
        },
        body: JSON.stringify(data),
    })
    .then(response => response.json())
    .then(result => console.log('分数同步成功:', result))
    .catch(error => console.error('同步失败:', error));
}

3. 后端视图逻辑错误修复

不要新建Mathgame实例,而是更新当前游戏的score字段,同时添加参数校验与异常处理:

def score(request, slug):
    if request.method != 'POST':
        return JsonResponse({'error': '仅支持POST请求'}, status=400)
    if not request.user.is_authenticated:
        return JsonResponse({'error': '请先登录'}, status=401)
    
    try:
        # 确保只能更新当前用户的游戏
        mathgame = Mathgame.objects.get(slug=slug, user=request.user)
    except Mathgame.DoesNotExist:
        return JsonResponse({'error': '游戏不存在'}, status=404)
    
    try:
        data = json.loads(request.body)
        score = data.get('score')
        if not isinstance(score, int) or score < 0:
            return JsonResponse({'error': '分数格式错误'}, status=400)
        
        # 更新现有游戏的分数
        mathgame.score = score
        mathgame.save()
        return JsonResponse({'score': score})
    except json.JSONDecodeError:
        return JsonResponse({'error': '请求数据格式错误'}, status=400)

4. 修复减法逻辑交换错误

原代码未正确交换两个数,导致可能出现负数结果,修正为:

if (number2 > number1) {
    // ES6解构交换,或使用临时变量
    [number1, number2] = [number2, number1];
}

5. 配置后端路由

在项目的urls.py中添加分数更新的路由:

from django.urls import path
from . import views

urlpatterns = [
    # 其他路由...
    path('mathgame/<slug:slug>/score/', views.score, name='score'),
]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 03:27:39