I'm looking for a way to store a question fetched from an external API to be stored in a model called Question
. My views.py
module does that by requesting data based on user's question difficulty choice, then renders what's fetched in a form. It's a pretty straightforward method but for some reason I get Field 'id' expected a number but got {'type': 'multiple'...}
.
I deleted a code that intended to create a another model singleton that was interfering with this implementation from last answer of this question. Then I ran ./manage.py makemigrations
and ./manage.py migrate
to reflect changes but the exception was again raised. After that, I deleted migrations.py and their cached files to run the same two commands and nothing changed.
Can anyone point out what I'm missing/doing wrong please?
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)
When using model
constructor, use keyword arguments! [1]
With keyword arguments, you can specify which arguments you are assigning to. For example:
Question(
type = content["type"],
difficulty = content["difficulty"],
category = content["category"],
question = content["question"],
correct_answer = content["correct_answer"],
incorrect_answers = content["incorrect_answers"]
)
You may also use dict
unpacking to construct a Question
. This will produce the arguments automatically:
Question(**question)
[1] In your example, the constructor of the class Question
takes question
which is a dict
and assigns this to its first argument id
.