質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.77%

urlパラメータに渡すIDによってページ遷移できる時と404になってしまう時がある

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 828

alpaka

score 166

 前提・実現したいこと

カテゴリーを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ページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

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 )かで意味合いが大きく変わるものと思います。

……というところがすっきりすれば解決に至られるのではないかと思うのですがいかがでしょう。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/21 11:56

    ご回答有り難うございます。
    models.pyを追記しました。頂いた内容を参考に試してみます。

    キャンセル

  • 2018/11/21 12:10 編集

    まずは、各 URL パターンの URL パラメータ `pk` が `QuizInfo` と `Quiz_category` のどちらの id に対応するのかを明確にする(=コード内でコメントで書く)ということをされるとよいかと思います。その上で、それぞれに対応する view の `model` アトリビュートや `get_queryset()` ・ `get_context_data()` をそれに揃える、ということをされてみてください。

    キャンセル

  • 2018/11/21 13:37

    pkをcategory_pkとquizinfo_pkと明示するようにしました。
    すると以下のエラーがでました。
    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 を返す処理」等を自ら行う必要があるものと思います。

    そのあたりは今すぐご対応されなくてもテストをされるときにお気づきになるかと思いますが、ご留意ください :)

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.77%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る