見出し画像

Webアプリ開発(Django+Vue.js+Docker)#10

こちらの記事の続きになります。

今回はテストをするページのテストをランダムに出題する部分を制作してみようと思います。

バックエンド

やることとしては、登録されているテストをユーザごとに抽出しフロントエンドに渡すという事です。

あとは、後々プルダウンメニューで出題範囲を絞ることもする予定なのでそこも一緒にやってしまいましょう。

やることはほかのページともう同じです。
dbから抽出してJSON形式に変更します。

def exam(request):
  if request.method == 'GET':
    """ dbに登録されているテストと科目を取り出す """
    param={}
    tests = Test.objects.filter(user=request.user)
    subjects = Test.objects.filter(user=request.user).distinct().values_list('subject', flat=True)
    
    test_list = []
    for test in tests:
      test_list.append({
        'id': test.id, 
        'subject': test.subject, 
        'que': test.que, 
        'ans': test.ans, 
        'total': test.total, 
        'correct': test.correct, 
        'percent': test.percent, 
      })
    
    subject_list = []
    for subject in subjects:
      if subject == "":
        subject_list.append({
          'subject': subject,
          'subject_name': "未分類"
        })
      else:
        subject_list.append({
          'subject': subject,
          'subject_name': subject
        })
    param['tests_list']=test_list
    param['subject_list']=subject_list
    return render(request, 'exam.html', param)

フロントエンド

バックエンドから送られてきたデータをVueで受け取り、ランダムに表示する部分を作成しましょう。

まず、受け取る部分は以下のようになります。


{% block content %}
<h1 style="font-size: 40px; margin-bottom: 1rem;"><b>テストする</b></h1>

<!-- メニューに戻る -->
<form method="get" action="{% url 'test:index' %}">
  <button type="submit" class="btn btn-secondary" style="font-size: 20px; margin-bottom: 2rem;">戻る</button>
</form>

{{ tests_list|json_script:"tests_list" }}
{{ subject_list|json_script:"subject_list" }}

{% endblock content %}

{% block script %}
<script>
  //DjangoからのデータをJSON形式で受け取る
  var testList = JSON.parse(document.getElementById('tests_list').textContent);
  var subjectList = JSON.parse(document.getElementById('subject_list').textContent);
</script>
{% endblock script %}

また、Vueを使用するので、お約束の部分も書いておきましょう。

<script>
  //DjangoからのデータをJSON形式で受け取る
  var testList = JSON.parse(document.getElementById('tests_list').textContent);
  var subjectList = JSON.parse(document.getElementById('subject_list').textContent);

  new Vue({
    el: '#show',
    delimiters: ['[[', ']]'],
    data: {   
    },
  })
</script>

では、準備が済んだのでこれから作っていきます。

まず、問題を表示する部分を作っていきましょう。
※Vueで動的に処理したいので<div id="show">で囲いましょう。

<!-- 初期画面は非表示 -->
<div id="show">
    <!-- 問題部分 -->
  <div class="card">
    <h5 class="card-header">
      <div>問題</div>
      <div><span class="badge bg-secondary">[[ test.subject ]]</span></div>
      <div>正答率:[[ test.percent ]]%</div>
    </h5>
    <div class="card-body">
      <h5 class="card-title"></h5>
      <p class="card-text">[[ test.que ]]</p>
    </div>
  </div>

  <!-- 解答部分 -->
  <div class="card">
    <h5 class="card-header">解答</h5>
    <div class="card-body">
      <h5 class="card-title"></h5>
      <p class="card-text">[[ test.ans ]]</p>
    </div>
    <p class="info">
      正答率:[[ test.percent ]]%<br>
      出題数:[[ test.total ]]
    </p>
  </div>
</div>

今回も、bootstrapのカードを使用しました。

それでは、この内容に合わせてVue関数を作っていきましょう。
と言っても簡単。以下の様に変更するだけです。

new Vue({
    el: '#show',
    delimiters: ['[[', ']]'],
    data: {
      test: testList[Math.floor(Math.random()*testList.length)],
    },
  })

もうtestにバックエンドから送られてきたテストの中からランダムに一つtestに格納するだけで大丈夫。

この状態でページを何回か行き来してみてください。
表示されているテストが変化していることが分かります。

しかし、これでは味気ないので、正解か不正解か判定するボタンを設置し、それを押すことでデータベースの正誤回数に反映され、同時に次の問題に行くという部分も作成して見ましょう。

フロントから正誤をバックエンドに送り、データベースの正解回数と正答率などを変更するためいつも通り、以下を追加します。

<p class="info">
    正答率:[[ test.percent ]]%<br>
    出題数:[[ test.total ]]
  </p>
  <form method="post" action="{%url 'test:exam' %}">
    {% csrf_token %}
    <input type="hidden" name="test_id" :value="[[ test.id ]]">
    <button type="submit" class="btn btn-primary" name="correct" style="font-size: 20px;">正解</button>
    <button type="submit" class="btn btn-danger" name="uncorrect" style="font-size: 20px;">不正解</button>
  </form>
</div>

メソッドにはPOSTを使用し、正答率を上書きするテストのidを送ります。
また、正解なのか不正解なのかのボタンも追加します。
※バックエンドでどっちなのか判断するために<button>タグ内のnameを忘れずに。

これでフロントエンドはおしまいです。

次はバックエンドを書きます。

バックエンド

POSTメソッドの処理を追加します。

  param['tests_list']=test_list
    param['subject_list']=subject_list
    return render(request, 'exam.html', param)
  elif request.method == 'POST':
    """ 正解数と出題数を変更する """
    test_id = request.POST.get('test_id')
    test = Test.objects.get(id=test_id)
    if "correct" in request.POST:
      test.correct+=1
      test.total+=1
    elif "uncorrect" in request.POST:
      test.total+=1

    """ パーセント計算 """
    test.percent=(test.correct/test.total)*100
    test.save()
    return HttpResponseRedirect(reverse('test:exam'))

内容は、フロントエンドから送られてきたテストidをもとにデータベースからテストを取得し、正解/不正解に応じて処理を行います。

そして、同じページに帰ります。(リダイレクト)

以上で終わり!

確認して見ましょう。
ボタンをポチポチ押していくと次々とランダムにテストが出題され、その時の正答率などもちゃんとアップデートされていることが分かります。


この記事が気に入ったらサポートをしてみませんか?