Django アプリケーション内で、データベースから取得したデータをグラフにして表示したいと思ったので方法を調べました。
ざっくりですが、取得した情報を元にグラフの SVG 画像を作成、そしてページに表出する方法を記載します。
1. テンプレートに img タグを追加
テンプレートファイルのグラフを表示したい場所に img タグを追加し、URL 逆引きで URL ディスパッチャを呼び出します。
<img src="{% url 'sample_django_app:yt_trend_svg' %}">
今回は sample_django_app の yt_trend_svg というディスパッチャを呼び出しています。
2. urls.py に URL ディスパッチャを追加
urls.py の urlpatterns リストの中に下記を追加し、yt_trend_svg が呼び出されたら views.py の get_trend_svg 関数を呼び出す様設定しています。
path('chart/', views.get_trend_svg, name="yt_trend_svg"),
3. views.py に SVG 描画の処理を追加
ビューで get_trend_svg 関数を定義し、データの取得、グラフの作成、SVG への変換をして最後に HttpResponse を返します。
処理の流れは下記の通りです。
- URL ディスパッチャからget_trend_svg が呼び出される
- YtTrendChannel モデルからデータを取り出す
- x と y のリストを作成
- make_chart 関数に x と y を渡しグラフを作成
- convert_to_svg 関数でグラフを SVG に変換
- HttpResponse として SVG を返す
import io
from django.http import HttpResponse
import matplotlib
matplotlib.use('Agg') # Matplotlib の backend を指定
import matplotlib.pyplot as plt
# 1. ルーターから get_trend_svg が呼び出される
def get_trend_svg(request):
def make_chart(x, y):
# setting up plots
plt.bar(x, y, color='#00d5ff')
plt.title("My Chart", color='#3407ba')
plt.xlabel("channel")
plt.ylabel("views")
def convert_to_svg():
# convert the plots to SVG
buffer = io.BytesIO() # バッファを作成
plt.savefig(buffer, format='svg', bbox_inches='tight') # バッファに一時保存
s = buffer.getvalue() # バッファの内容を s に渡す
buffer.close() # バッファはクローズ
return s # s を返す
# 2. YtTrendChannel モデルからデータを取り出す
channels = YtTrendChannel.objects.filter(data_date='20210104').order_by('channel_name')[:10]
# 3. x と y のリストを作成
x = [channel.channel_name for channel in channels]
y = [int(channel.view_count) for channel in channels]
# 4. make_chart 関数に x と y を渡しグラフを作成
make_chart(x, y)
svg = convert_to_svg() # 5. グラフを SVG に変換
plt.cla()
# 6. HttpResponse として SVG を返す
return HttpResponse(svg, content_type='image/svg+xml')
convert_to_svg 関数の中でコメントを色々書いていますが、メモリ上に一時ファイルを保存できるため、実ファイルが作られなくて済むんだそうです。
すると下記の様にチャートが表示されます。表示がだいぶ崩れてますがこれは後ほど調整するということで。。。

