在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
相关产品推荐
相关产品推荐

