閲覧ありがとうございます。
概要
現在、日本語と英語から、簡単なクイズのアプリを作ろうとしています。実現したい機能自体は関数ベースのビューで実装できたのですが、Djangoの利点である汎用クラスベースビューを使った実装を実現したいと思い質問させていただきました。
実現したい機能・要件
htmlファイルのform中のinputタグから「間違った英語」と「間違った日本語」を受信し、データベースの難易度を上昇。その後、間違った間違った英語と日本語を一覧として表示。以上の機能を、汎用クラスベースビューで実現したい。
思い当たる原因
- そもそもgeneric.ListViewを利用すべき動作ではない
- generic.ListViewを利用するときに、定義すべきメソッドが定義されていない
- Mixinと多重継承への理解が不十分
コード
丸々載せていたら文字制限にひっかかったので、動作に関係がある部分のみ抜き出すと、以下のようになります。リクエストしてもらえれば、quiz作成部分等も追記します。
models.py
日本語と英語とをM:N対応で定義しています。今回のプログラムではデータを取得することにのみ利用しており、メソッド等は使っていません。
import datetime from django.db import models from django.utils import timezone class JapaneseWord(models.Model): """ 日本語とそれに伴う情報を規定する Attributes ---------- japanese_mean: CharField 英単語に対応する日本語の意味 difficulty: IntegerField 日本語の難易度 Note ---- EnglishWordとJapaneseWordはM:Nで対応している """ japanese_mean = models.CharField(max_length=50) pub_date = models.DateTimeField('date published', default=timezone.now) difficulty = models.IntegerField(default=0) def __str__(self): return self.japanese_mean class EnglishWord(models.Model): """ 英単語とそれに伴う情報を定義する。 Attributes ---------- spell: CharField 英単語のスペル pub_date: DateTimeField 英単語の作成日 difficulty: IntegerField 英単語の難易度 japanese_words: ManyToManyField 英単語と日本語のM:N対応 """ spell = models.CharField(max_length=30) pub_date = models.DateTimeField('date published', default=timezone.now) difficulty = models.IntegerField(default=0) japanese_words = models.ManyToManyField(JapaneseWord, verbose_name='日本語', blank=True) def __str__(self): return self.spell def was_published_recently(self): now = timezone.now() return now - datetime.timedelta(days=1) <= self.pub_date <= now was_published_recently.admin_order_field = 'pub_date' was_published_recently.boolean = True was_published_recently.short_description = 'Published Recently?'
テンプレートのquiz.html
コンテキストからクイズを表示するためのhtmlファイルです。JavaScriptを使って解答を集計し、form内に追加したinput要素を経由して、postで値を送信しています。
html
1{% extends 'wordapp/base.html' %} 2 3{% load static %} 4 5{% block title %}my-wordapp_quiz_dummy_sample{% endblock %} 6 7{% block hero_unit %} 8 <h1 class="h3">英単語のクイズページ</h1> 9{% endblock %} 10 11{% block body %} 12 <h3>クイズ</h3> 13 <form> 14 {% csrf_token %} 15 <input type="hidden" id="quiz-length" value="{{ quizzes | length }}"> 16 {% for quiz in quizzes %} 17 <div class="quiz"> 18 <p class="question_english">{{ quiz.word }}</p> 19 <p class="word_id">word_id: {{ quiz.word_id }}</p> 20 <p class="answer">answer: {{ quiz.answer }}</p> 21 <input type="hidden" id="answer-{{ quiz.id }}" value="{{ quiz.answer }}"> 22 <input type="hidden" id="id-{{ quiz.id }}" value="{{ quiz.word_id }}"> 23 <ul> 24 {% for choice in quiz.choices %} 25 <li><input id="choice-{{ quiz.id }}-{{ forloop.counter }}" class="choice" type="radio" value="{{ choice }}" 26 name="quiz-{{ quiz.id }}"> 27 <label for ="choice-{{ quiz.id }}-{{ forloop.counter }}">{{ choice }}</label></li> 28 {% endfor %} 29 </ul> 30 </div> 31 {% endfor %} 32 <input type="button" value="集計" onclick="aggregate()"> 33 <p>正解した問題数: <span id="number-of-right-answer"></span></p> 34 <p>間違えた問題: <span id="wrong-problem"></span></p> 35 <p>間違えた選択肢に含まれる日本語: <span id="wrong_japanese_answers"></span></p> 36 </form> 37 38 <script> 39 function aggregate() { 40 let result = 0; 41 let quiz_length = document.getElementById('quiz-length').value; 42 let wrong_answer_word_id = []; 43 // 間違えた日本語 44 let wrong_japanese_answers = []; 45 46 for (let i = 1; i <= quiz_length; i++){ 47 let answer = document.getElementById(`answer-${i}`).value; 48 let choices = document.getElementsByName(`quiz-${i}`); 49 let word_id = document.getElementById(`id-${i}`).value; 50 51 for (var j = 0; j < choices.length; j++){ 52 if (choices[j].checked) { 53 if (answer == choices[j].value){ 54 result++; 55 } 56 else { 57 // 間違えた英語 58 wrong_answer_word_id.push(word_id); 59 // 間違えた日本語追加 60 /* 61 ここでノードリストになっている 62 for文で全展開 63 */ 64 for(let k=0; k < choices.length; k++){ 65 wrong_japanese_answers.push(choices[k].value); 66 } 67 } 68 break; 69 } 70 } 71 } 72 document.getElementById("number-of-right-answer").textContent = result; 73 document.getElementById("wrong-problem").textContent = wrong_answer_word_id; 74 //間違えた日本語の出力確認(重複排除) 75 wrong_answer_words_array = Array.from(new Set(wrong_japanese_answers)); 76 document.getElementById("wrong_japanese_answers").textContent = wrong_answer_words_array; 77 78 let form = document.createElement('form'); 79 let wrong_question = []; 80 81 form.method = "POST"; 82 form.action = "{% url 'wordapp:quiz_feedback_sample' %}"; 83 form.id = "wrong_answer_word"; 84 85 // 間違えた英語のinput 86 let wrong_english_input = document.createElement('input'); 87 wrong_english_input.type = 'hidden'; 88 wrong_english_input.name = 'wrong_english_word_id'; 89 wrong_english_input.value = wrong_answer_word_id; 90 // 間違えた日本語のinput 91 let wrong_japanese_input = document.createElement('input'); 92 wrong_english_input.type = 'hidden'; 93 wrong_japanese_input.name = 'wrong_japanese_words'; 94 wrong_japanese_input.value = wrong_answer_words_array; 95 //csrf_tokenの追加手動 96 let csrf_element = document.createElement('input'); 97 csrf_element.type = 'hidden'; 98 csrf_element.name = 'csrfmiddlewaretoken'; 99 csrf_element.value = '{{ csrf_token }}'; 100 101 form.appendChild(csrf_element); 102 form.appendChild(wrong_english_input); 103 form.appendChild(wrong_japanese_input); 104 document.body.appendChild(form); 105 106 form.submit(); 107 } 108 109 </script> 110{% endblock %}
views.py中のquiz_feedback関数
上記のhtmlファイルから送信されたinput要素を取得し、データベースの読み取り用に加工した後、データを操作しています。
取得直後のデータは、
wrong_english_word_id: 10,9,11 wrong_japanese_words: (ある場所から他へ)持っていく,作る,(手などで)取る,もたらす
のような文字列になっており、データベースの読み取り用に加工した後は、
wrong_english_word_id_list: [10, 9, 11],type: <class 'list'> wrong_japanese_words_list: ['(ある場所から他へ)持っていく', '作る', '(手などで)取る', 'もたらす'],type: <class 'list'>
のようなリスト型のデータになっています。
python
1def quiz_feedback(request): 2 """ 3 クイズのフィードバックを受け取る関数 4 """ 5 wrong_english_word_id = request.POST["wrong_english_word_id"] 6 wrong_english_word_id_list = [ 7 int(wrong_english_word_id) for wrong_english_word_id in wrong_english_word_id.split(",") 8 ] 9 wrong_japanese_words = request.POST["wrong_japanese_words"] 10 wrong_japanese_words_list = wrong_japanese_words.split(",") 11 wrong_english_words_queryset = EnglishWord.objects.filter(pk__in=wrong_english_word_id_list) 12 wrong_japanese_words_queryset = JapaneseWord.objects.filter(japanese_mean__in=wrong_japanese_words_list) 13 14 context = { 15 "wrong_english_words": wrong_english_words_queryset, 16 "wrong_japanese_words": wrong_japanese_words_queryset 17 } 18 19 return render( 20 request, 21 "wordapp/quiz_feedback_sample.html", 22 context 23 )
views.py内で試作した汎用クラスビューを利用した実装(未動作)
上記のquiz_feedback関数を改めて自分で見なおしたときに、結局やっていることは要素の羅列であると思ったので、generic.ListViewを使った実装を試みました。
必要な動作は一通り書き込んだつもりだったのですが、実際にurls.pyを変更してアクセスしてみると、Method Not Allowedというエラーが表示されます。
おそらくPOSTの取得についてなにがしかの設定をしていないため、そうなっているとおもうのですが、なにを実装すべきなのかがわかりませんでした。
python
1class QuizFeedBack(generic.ListView): 2 """ 3 クイズのフィードバックを受けて、間違えた英語と日本語の一覧を表示する 4 """ 5 context_object_name = "wrong_english_word_list" 6 model = EnglishWord 7 template_name = "quiz_feedback_sample.html" 8 9 def get_queryset(self, request, *args, **kwargs): 10 wrong_english_word_post = request.POST["wrong_english_word_id"] 11 wrong_english_word_id_list = [ 12 int(wrong_english_word_id) for wrong_english_word_id in wrong_english_word_post.split(",") 13 ] 14 queryset_to_return = EnglishWord.objects.filter(pk__in=wrong_english_word_id_list) 15 print(queryset_to_return) 16 return queryset_to_return 17 18 def get_context_data(self, request, *args, **kwargs): 19 context = super().get_context_data(*args, **kwargs) 20 wrong_japanese_word_post = request.POST["wrong_japanese_words"] 21 wrong_japanese_words_list = wrong_japanese_word_post.split(",") 22 wrong_japanese_word_queryset = JapaneseWord.objects.filter(japanese_mean__in=wrong_japanese_words_list) 23 print(wrong_japanese_word_queryset) 24 context["wrong_japanese_words"] = wrong_japanese_word_queryset 25 return context
また、下記のpost関数をメソッドとして追加してみましたが、変わらず動作しませんでした。
def post(self, request, *args, **kwargs): print(request.POST)
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/03/24 00:00
2021/03/24 01:56 編集
2021/03/26 16:06
2021/03/26 22:29