【Python】Django:足し算テストアプリを作成(Reportlabを使ってPDFで足し算テストを出力)

  • URLをコピーしました!
目次

Django

前回、Reportlabを使って、Djangoで作成したウェブサイトからPDFファイルを作成する方法を学習しました。

今回は足し算アプリ上に表示されたランダムな問題をPDFに出力するということをしていきます。

修正するファイルは以下の2つです。

  • /webapp/tashizan1/templates/tashizan1/index.html
  • /webapp/tashizan1/views.py
webapp
├── ...
└── tashizan1
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── forms.py    
    ├── migrations
    │   └── __init__.py
    ├── models.py
    ├── templates
    │   └── tashizan1
    │       ├── answercheck.html
    │       └── index.html                <- これ
    ├── tests.py
    ├── urls.py
    └── views.py                          <- これ

それでは始めていきましょう。

/webapp/tashizan1/templates/tashizan1/index.html

まずはランダムに作成された問題の値をviews.pyのpdf関数に移す必要があります。

値を2ページ間で受け渡しする方法はこちらのページで解説していますが、<input type=’hidden’ >を用います。

それで修正してみたindex.htmlがこちら。

{% extends "basetemplates/base.html" %}

{% block title %}
<title>{{ title }}|足し算テスト1</title>
{% endblock %}

{% block content %}
<h1>足し算テスト1</h1>
<form action="{% url 'answercheck' %}" method="post">
    {% csrf_token %}
    <div class="row">
        <div class="col">
            <table class="mx-auto">
                <tr><td>\(1.\)</td><td>\({{ qa.0 }} + {{ qb.0 }} = \){{ forms.answer0 }}</td></tr>
                <tr><td>\(2.\)</td><td>\({{ qa.1 }} + {{ qb.1 }} = \){{ forms.answer1 }}</td></tr>
                <tr><td>\(3.\)</td><td>\({{ qa.2 }} + {{ qb.2 }} = \){{ forms.answer2 }}</td></tr>
                <tr><td>\(4.\)</td><td>\({{ qa.3 }} + {{ qb.3 }} = \){{ forms.answer3 }}</td></tr>
                <tr><td>\(5.\)</td><td>\({{ qa.4 }} + {{ qb.4 }} = \){{ forms.answer4 }}</td></tr>
                <tr><td>\(6.\)</td><td>\({{ qa.5 }} + {{ qb.5 }} = \){{ forms.answer5 }}</td></tr>
                <tr><td>\(7.\)</td><td>\({{ qa.6 }} + {{ qb.6 }} = \){{ forms.answer6 }}</td></tr>
                <tr><td>\(8.\)</td><td>\({{ qa.7 }} + {{ qb.7 }} = \){{ forms.answer7 }}</td></tr>
                <tr><td>\(9.\)</td><td>\({{ qa.8 }} + {{ qb.8 }} = \){{ forms.answer8 }}</td></tr>
                <tr><td>\(10.\)</td><td>\({{ qa.9 }} + {{ qb.9 }} = \){{ forms.answer9 }}</td></tr>
                </table>
                {% for qa_val in qa %}
                    <input type='hidden' name='qa' value={{qa_val}}>
                {% endfor %}
                {% for qb_val in qb %}
                    <input type='hidden' name='qb' value={{qb_val}}>
                {% endfor %}
        </div>   
    </div>
    <p style="margin:2rem 0rem 0rem 0rem;"></p>
    <p style='text-align: center;'><input type='submit' value="答え合わせ"></p>
</form>
<form action="{% url 'pdf' %}" method="post">
    {% csrf_token %}
    {% for qa_val in qa %}
    <input type='hidden' name='qa' value={{qa_val}}>
    {% endfor %}
    {% for qb_val in qb %}
        <input type='hidden' name='qb' value={{qb_val}}>
    {% endfor %}
    <p style='text-align: center;'><input type='submit' value="PDF作成"></p>
</form>
{% endblock %}

足し算の問題を受け渡している部分はこの部分です。

    {% for qa_val in qa %}
    <input type='hidden' name='qa' value={{qa_val}}>
    {% endfor %}
    {% for qb_val in qb %}
        <input type='hidden' name='qb' value={{qb_val}}>
    {% endfor %}

元々リスト形式だった「qa」「qb」を一度for関数で一つずつ値を取り出し、受け渡すことで、受け取った際にリスト形式で受け取ることができます。

ちなみに同じプログラムがもう一度出ていることに気づいたでしょうか?

プログラムの中盤の</table>の後にも同じプログラムが出ていますが、これはviews.pyのanswercheck関数に受け渡すためのものなので、め、<form action=”{% url ‘answercheck’ %}” method=”post”> 〜 </form>の間に先ほどのループが記述されています。

今回はpdf関数に受け渡すため、<form action=”{% url ‘pdf’ %}” method=”post”> 〜 </form>の間にもう一度記述する必要があるのです。

これでpdf関数に足し算テストの問題の値を受け渡す準備ができました。

/webapp/tashizan1/views.py

次に渡された値を受け取り、PDFにするため、views.pyのpdf関数を修正していきます。

views.pyを全て記述すると長くなってしまうので、pdf関数だけを記述します。

def pdf(request):
    params = definitions.readjson()
    qa = request.POST.getlist('qa')
    qb = request.POST.getlist('qb')

    ans = []
    for qa_val, qb_val in zip(qa,qb):
        ans.append(int(qa_val) + int(qb_val))

    response = HttpResponse(content_type='application/pdf')
    pdf_name = 'tashizan1.pdf'
    response['Content-Disposition'] = 'attachment; filename=' + pdf_name

    pdf_size = portrait(A4)

    pdf_file = canvas.Canvas(response, pagesize=pdf_size, bottomup=False)

    pdf_file.setFont('Helvetica', 25)

    pdf_file.drawString(25*mm, 20*mm, "Date:       /       /       name:")
    
    for i, val in enumerate(zip(qa, qb)):
        vertical = 20+20*(i+1)
        horizontal = 25
        pdf_file.drawString(horizontal*mm, vertical*mm, f"{i+1}.  {val[0]} + {val[1]} =")
    
    pdf_file.setFont('Helvetica', 10)
    pdf_file.drawString(125*mm, 250*mm, '3PySci: https://3pysci.com')
    pdf_file.drawString(125*mm, 290*mm, f'1.{ans[0]} 2.{ans[1]} 3.{ans[2]} 4.{ans[3]} 5.{ans[4]} 6.{ans[5]} 7.{ans[6]} 8.{ans[7]} 9.{ans[8]} 10.{ans[9]}')

    pdf_file.save()

    return response

まず最初に足し算テストの問題の値「qa」「qb」を取得し、その足し算の答えを「ans」に格納しています。

params = definitions.readjson()
    qa = request.POST.getlist('qa')
    qb = request.POST.getlist('qb')

    ans = []
    for qa_val, qb_val in zip(qa,qb):
        ans.append(int(qa_val) + int(qb_val))

こちらは前回解説した部分で、アクセスした場合にダウンロードするのか、表示するのか、またPDFファイルのサイズや向きなどを決めています。

    response = HttpResponse(content_type='application/pdf')
    pdf_name = 'tashizan1.pdf'
    response['Content-Disposition'] = 'attachment; filename=' + pdf_name

    pdf_size = portrait(A4)

    pdf_file = canvas.Canvas(response, pagesize=pdf_size, bottomup=False)

次に問題の前にテストの日時と名前を書く欄を作ってみました。

    pdf_file.setFont('Helvetica', 25)

    pdf_file.drawString(25*mm, 20*mm, "Date:       /       /       name:")

まず「.setFont()」で使用するフォント、フォントサイズを指定しています。

今回フォントはMac内に標準で入っている「Helevetica」を使っていますが、これは英字フォントなので、日本語は入っていません。

前にGoogle Fontから使用が比較的自由なSILライセンスのフォントをダウンロードしていますので、次回そのフォントへの移行をしましょう。

今回はあくまでも問題を表示することに集中していきます。

「.drawString」は前回勉強していて、「.drawString(Xの位置、Yの位置、”テキスト”)」で、テキストをX、Yの位置に配置するというコマンドでした。

問題を配置しているのがこの部分。

    for i, val in enumerate(zip(qa, qb)):
        vertical = 20+20*(i+1)
        horizontal = 25
        pdf_file.drawString(horizontal*mm, vertical*mm, f"{i+1}.  {val[0]} + {val[1]} =")

enumerate関数を使って、ループの回数と「qa」「qb」を順に取得しています。

ちなみにenumerate関数に関してはこちらの記事で解説していますので、よかったらどうぞ。

「vertical」と「horizontal」はそれぞれ縦の位置と横の位置で、横の位置は一定なので定数(25)、縦の位置は少しずつ下げていくため、ループの回数「i」を使って数値が順々に大きくなるようにしています。

ちなみにenumerate関数のループ回数は「0」から始まるので、「vertical = 20+20*i」だと20から始まってしまいます。

そこで「vertical = 20+20*(i+1)」、つまり「i+1」と1を足すことで40から開始するように調整しています。

(初歩的なことですが、これに気付くのに小一時間かかったのは内緒)

「.drawString()」は先ほど同様、テキストを配置しているコマンドです。

ということで1問ずつ配置する場所を変えて、「.drawString()」で配置していっているというわけです。

最後に答えを小さく紙の下の方に記載しておき、答え合わせが楽にできるようにしています。

    pdf_file.setFont('Helvetica', 10)
    pdf_file.drawString(125*mm, 250*mm, '3PySci: https://3pysci.com')
    pdf_file.drawString(125*mm, 290*mm, f'1.{ans[0]} 2.{ans[1]} 3.{ans[2]} 4.{ans[3]} 5.{ans[4]} 6.{ans[5]} 7.{ans[6]} 8.{ans[7]} 9.{ans[8]} 10.{ans[9]}')

先ほど「.setFont()」で使用するフォントとフォントサイズを指定しましたが、その指定は次の「.setFont()」が出てくるまで有効です。

ということでここでフォントサイズを小さくしたいので、再度「.setFont()」を使っているというわけです。

そして宣伝用に3PySciのURLを配置し、その下に答えを配置しています。

最後に作成したPDFを出力し、returnでレスポンスとして返しています。

    pdf_file.save()

    return response

ここまでできたらサーバーを起動し、アクセスしてみましょう。

「PDF作成」ボタンを押して、作成したPDFをダウンロードしてみるとこんな感じです。

A4、縦向きで問題が10問、ちょうどいい感じで配置することができました。

次回は今回後回しにした日本語フォントを使う、ということを試してみましょう。

ではでは今回はこんな感じで。

よかったらシェアしてね!
  • URLをコピーしました!

コメント

コメントする

目次