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)
其他潜在问题与修正
incorrect_answers字段存储问题
API返回的incorrect_answers是列表类型,但模型中incorrect_answers是TextField,直接存入列表会导致数据格式错误。需要先将列表序列化为字符串,比如用json.dumps:
import json # 在render_question函数中修改question字典 question = { # ...其他字段 "incorrect_answers": json.dumps(content["incorrect_answers"]), }
- 视图函数缺少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目录下查找。
- 测试用例的潜在问题
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
相关产品推荐
相关产品推荐

