前提・実現したいこと
カテゴリーをIDで指定し、カテゴリーに属するクイズからランダムに出題するアプリを作っています。
しかし、カテゴリーごとにcontextをテンプレートに渡せるものと、404になるものがあります。
画面はquiz_list.html(カテゴリーの選択)→question.html(スタート画面)→selected_quiz.html(クイズ画面)と遷移します。
今回の場合ですとカテゴリーIDが2はうまくいきますがカテゴリーIDが2以外だと404になります。
カテゴリーIDが違うだけな気がするのですが、何が成否を分けているのか全くわかりません。ご教授お願いします。
発生している問題・エラーメッセージ
Page not found (404)
Request Method: GET
Request URL: http://localhost:8000/quiz/3/selected_quiz/
Raised by: quiz.views.SelectedQuizView
No quiz info found matching the query
該当のソースコード
urls.py
urlpatterns = [
re_path(r'(?P<pk>\d+)/question', views.QuestionView.as_view(), name='question'),
re_path(r'(?P<pk>\d+)/list', views.QuizListView.as_view(), name='quiz_list'),
re_path(r'(?P<pk>\d+)/selected_quiz', views.SelectedQuizView.as_view(), name='selected_quiz'),
]
views.py
class QuizListView(generic.ListView):
model = QuizInfo
template_name = 'quiz/quiz_list.html'
context_object_name = 'quiz_list'
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(**kwargs)
context['question'] = Quiz_category.objects.filter(id=self.kwargs['pk']).first()
return context
def get_queryset(self):
return QuizInfo.objects.filter(quiz_category_id=self.kwargs['pk'])
class QuestionView(generic.TemplateView):
template_name = 'quiz/question.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['question'] = Quiz_category.objects.filter(id=self.kwargs['pk']).first()
return context
class SelectedQuizView(generic.DetailView):
model = QuizInfo
template_name = 'quiz/selected_quiz.html'
context_object_name = 'quiz'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# カテゴリー別のクイズ一覧からランダムに一つ選択して出題する。
context['quiz'] = random.choice(QuizInfo.objects.filter(quiz_category_id=self.kwargs['pk']))
return context
models.py
# 問題の所属するカテゴリ-(ex 医療、動物、工学など)
class Quiz_category(models.Model):
category = models.CharField(max_length=200)
parent_id = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE)
register_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.category
# クイズに関する情報をすべて格納する
class QuizInfo(models.Model):
quiz_name = models.CharField(max_length=200)
quiz_text = models.TextField(blank=True, default='')
publisher = models.ForeignKey(User, related_name='publisher', on_delete=models.CASCADE) #クイズを投稿したユーザ
correct_answer_percentage = models.IntegerField(default=0)
quiz_category_id = models.ForeignKey(Quiz_category, on_delete=models.CASCADE, related_name='categories')
img = models.ImageField(upload_to='img/',blank=True, default='')
quiz_img = ImageSpecField(source='img', format='JPEG', processors=[ResizeToFill(1000,800)], options={'quality': 50})
quiz_thumbnail = ImageSpecField(source='img', format='JPEG', processors=[ResizeToFill(100,100)], options={'quality': 50})
register_time = models.DateTimeField(auto_now_add=True)
is_public = models.BooleanField(default=True)
def __str__(self):
return self.quiz_name
quiz_list.html
<a href="{% url 'quiz:question' question.id %}">Random Questions</a>
question.html
<h1>Question Setting</h1>
<button class="btn btn-secondary">
<a href="{% url 'quiz:selected_quiz' question.pk %}">START</a>
</button>
selected_quiz.html
<h1>Quiz Name</h1>
<h1>{{ quiz.quiz_name }}</h1>
<h5>{{ quiz.quiz_difficulty }}</h5>
<img src="{{ quiz.quiz_img.url }}" alt="img">
試したこと
○views.py
・直接カテゴリーIDを指定してみる。
context['quiz'] = random.choice(QuizInfo.objects.filter(quiz_category_id=3))←カテゴリーID2のカテゴリーからページ遷移していくとカテゴリーID3の問題が出題されることがわかりました。
ID3のカテゴリーから遷移していくと404のままでした。
・printでcontext['quiz']を出力してみる
カテゴリーIDごとに正しく値が入っていました。
○question.html
・question.pkを出力してみる
→選択したカテゴリーIDがquestion.pkに入っていました。
○直接アクセス
直接URLにアクセス
http://localhost:8000/quiz/2/selected_quiz/→成功
http://localhost:8000/quiz/3/selected_quiz/→404
補足情報(FW/ツールのバージョンなど)
Django2.1
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
0
Model の定義を書かれていませんが、おそらく QuizInfo
と Quiz_category
という 2 つの Model を定義されているものと思います。これらの区別で少し混乱されていらっしゃるのではないかと思いました。
というのは、こちら ↓ は「関連した quiz_category
(= Quiz_category
)の pk
が URL パラメータの pk
と等しい QuizInfo
のリスト」を取得しようとされています。
context['quiz'] = random.choice(QuizInfo.objects.filter(quiz_category_id=self.kwargs['pk']))
他方、こちら ↓ のルーティング設定で「ページを表示するか 404 を返すか」が決まるのは「 QuizInfo
そのものの pk
が URL パラメータの pk
と等しい QuizInfo
があるかどうか」です。
re_path(r'(?P<pk>\d+)/selected_quiz', views.SelectedQuizView.as_view(), name='selected_quiz'),
QuizInfo
と QuizCategry
は別物なので、どちらの pk
( id
)かで意味合いが大きく変わるものと思います。
……というところがすっきりすれば解決に至られるのではないかと思うのですがいかがでしょう。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.32%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2018/11/21 11:56
models.pyを追記しました。頂いた内容を参考に試してみます。
2018/11/21 12:10 編集
2018/11/21 13:37
すると以下のエラーがでました。
Generic detail view SelectedQuizView must be called with either an object pk or a slug in the URLconf.
そのためViewの種類を変えたたところ、カテゴリーID毎に問題が出力されるようになりました。
class SelectedQuizView(generic.DetailView):
↓
class SelectedQuizView(generic.TemplateView):
ご回答いただきありがとうございました。後で見てわかるようにシンプルかつ明示的にするのがよいですね。
2018/11/21 15:45
ちなみに、ベースクラスを `DetailView` から `TemplateView` に変更をすると、 `DetailView` がやってくれていた「 URL で指定された pk に該当するレコードが見つからなかったら 404 を返す処理」等を自ら行う必要があるものと思います。
そのあたりは今すぐご対応されなくてもテストをされるときにお気づきになるかと思いますが、ご留意ください :)