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

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

新規登録して質問してみよう
ただいま回答率
85.35%
Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

POST

POSTはHTTPプロトコルのリクエストメソッドです。ファイルをアップロードしたときや入力フォームが送信されたときなど、クライアントがデータをサーバに送る際に利用されます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

1464閲覧

Django: 2つのモデルを取り入れたリストビューを汎用クラスベースで実現したい

kensoon

総合スコア48

Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

POST

POSTはHTTPプロトコルのリクエストメソッドです。ファイルをアップロードしたときや入力フォームが送信されたときなど、クライアントがデータをサーバに送る際に利用されます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/03/20 00:27

編集2021/03/20 00:28

閲覧ありがとうございます。

概要

現在、日本語と英語から、簡単なクイズのアプリを作ろうとしています。実現したい機能自体は関数ベースのビューで実装できたのですが、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)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

一方のモデルでListViewを実装して、get_context_dataで、もう一方のモデルインスタンスのQuerySetを渡すくらいでしょうか。
両方のモデルでページネーションするとなると、ややこしそうですが。

投稿2021/03/22 07:03

hasami

総合スコア1277

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kensoon

2021/03/24 00:00

ご回答ありがとうございます。 質問した後、汎用ビューの実装について少し調べてみた結果、generic.ListViewにはpostに対応したメソッドが継承されていないようでした。おっしゃっているペジネーションについても問題の数を増やすと困りそうなので、とりあえずは関数ビューで妥協しつつ、汎用ビューの実装について学んでいくことにします。 改めて、ご対応ありがとうございました。
hasami

2021/03/24 01:56 編集

ListViewはdjango.views.generic.Viewから継承されているため、dispatchメソッドによりpostメソッドは呼ばれます。
kensoon

2021/03/26 16:06

コメントありがとうございます。 ということは単純にQuizFeedbackクラスのdef postの実装が間違っているということになるのでしょうか。 https://intellectual-curiosity.tokyo/2019/02/27/django%E3%81%A7get%EF%BC%8Fpost%E3%81%8B%E3%82%89%E5%80%A4%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/ こちらのサイトのdef post部分を参考に仮引数を設定したみたのですが、どうにも根本的に間違っているような気がしてなりません。
hasami

2021/03/26 22:29

postが動作していないと、判断した理由は何ですか。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問