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

Django存储API题目到Question模型时出现id字段类型错误

问题描述

从外部API获取题目并存入Django的Question模型时,触发错误:Field 'id' expected a number but got {'type': 'multiple'...}。已删除干扰功能的旧模型代码,执行./manage.py makemigrations和./manage.py migrate同步变更,甚至删除migrations文件和缓存后重新执行命令,问题仍存在。

相关代码

models.py

from django.db import models


class Question(models.Model):
    type = models.TextField()
    difficulty = models.TextField()
    category = models.TextField()
    question = models.TextField()
    correct_answer = models.TextField()
    incorrect_answers = models.TextField()

views.py

from django.shortcuts import render, HttpResponse
from .forms import QuestionForm, QuestionLevelForm
from urllib.request import URLError
from .models import Question
import requests

def process_question(request):
    if "level" in request.POST:
        return fetch_question(request)
    elif "answer" in request.POST:
        return check_answer(request)
    else:
        form = QuestionLevelForm()
        return render(request, "log/question.html", {"form": form})


def fetch_question(request):
    match request.POST["difficulty"]:
        case "easy":
            url = "https://opentdb.com/api.php?amount=1&category=9&difficulty=easy&type=multiple"
        case "medium":
            url = "https://opentdb.com/api.php?amount=1&category=9&difficulty=medium&type=multiple"
        case "hard":
            url = "https://opentdb.com/api.php?amount=1&category=9&difficulty=hard&type=multiple"

    try:
        response = requests.get(url)
    except URLError as e:
        HttpResponse("Couldn't fetch data, try again")
    else:
        render_question(request, response.json())


def render_question(request, response):
    content = response["results"][0]
    question = {
        "type": content["type"],
        "difficulty": content["difficulty"],
        "category": content["category"],
        "question": content["question"],
        "correct_answer": content["correct_answer"],
        "incorrect_answers": content["incorrect_answers"],
    }
    form = QuestionForm(question)
    model = Question(question)
    model.save()
    context = {"question": question, "form": form}
    render(request, "./log/templates/log/question.html", context)

test_views.py

from django.http import HttpRequest
from django.test import TestCase, Client
from .. import views

client = Client()


class QuestionTest(TestCase):
    def test_page_load(self):
        response = self.client.get("/log/question")
        self.assertEqual(response["content-type"], "text/html; charset=utf-8")
        self.assertTemplateUsed(response, "log/question.html")
        self.assertContains(response, "Choose your question level", status_code=200)

    def test_fetch_question(self):
        request = HttpRequest()
        request.method = "POST"
        request.POST["level"] = "level"
        request.POST["difficulty"] = "hard"
        request.META["HTTP_HOST"] = "localhost"
        response = views.process_question(request)
        self.assertEqual(response.status_code, 200)
错误原因与修正方案

核心错误:模型实例化传参方式错误

在render_question函数中,创建Question实例时直接传入字典question:

model = Question(question)

Django模型的构造函数默认第一个参数是pk(即数据库的id字段),你把整个字典传给了这个参数,导致Django试图把字典解析成数字类型的id,因此触发Field 'id' expected a number but got {...}错误。

修正方法:使用关键字参数解包(**)传入字典:

model = Question(**question)

其他潜在问题与修正

  1. incorrect_answers字段存储问题
    API返回的incorrect_answers是列表类型,但模型中incorrect_answers是TextField,直接存入列表会导致数据格式错误。需要先将列表序列化为字符串,比如用json.dumps:
import json

# 在render_question函数中修改question字典
question = {
    # ...其他字段
    "incorrect_answers": json.dumps(content["incorrect_answers"]),
}
  1. 视图函数缺少return语句
  • fetch_question中的except块里,HttpResponse("Couldn't fetch data, try again")没有加return,导致异常发生时视图不会返回响应。
  • render_question最后一行的render(...)没有加return,导致视图执行完后返回None,Django会抛出服务器错误。

修正后的fetch_question和render_question:

def fetch_question(request):
    match request.POST["difficulty"]:
        case "easy":
            url = "https://opentdb.com/api.php?amount=1&category=9&difficulty=easy&type=multiple"
        case "medium":
            url = "https://opentdb.com/api.php?amount=1&category=9&difficulty=medium&type=multiple"
        case "hard":
            url = "https://opentdb.com/api.php?amount=1&category=9&difficulty=hard&type=multiple"

    try:
        response = requests.get(url)
    except URLError as e:
        return HttpResponse("Couldn't fetch data, try again")
    else:
        return render_question(request, response.json())


def render_question(request, response):
    content = response["results"][0]
    question = {
        "type": content["type"],
        "difficulty": content["difficulty"],
        "category": content["category"],
        "question": content["question"],
        "correct_answer": content["correct_answer"],
        "incorrect_answers": json.dumps(content["incorrect_answers"]),
    }
    form = QuestionForm(question)
    model = Question(**question)
    model.save()
    context = {"question": question, "form": form}
    return render(request, "log/question.html", context)

注意:render函数的模板路径不需要写全路径,直接用"log/question.html"即可,Django会自动在各app的templates目录下查找。

  1. 测试用例的潜在问题
    test_fetch_question中直接使用HttpRequest构造请求,不如用Client.post更符合Django测试规范,修正后的测试用例:
def test_fetch_question(self):
    response = self.client.post("/log/question", {"level": "level", "difficulty": "hard"})
    self.assertEqual(response.status_code, 200)
总结

核心错误是模型实例化时传参方式错误,使用**question解包字典即可解决。同时修复视图中缺少return、数据格式不匹配等潜在问题,确保功能正常运行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 12:28:19