Matplotlib で描画したグラフを SVG 化して Django で表示する方法

Django アプリケーション内で、データベースから取得したデータをグラフにして表示したいと思ったので方法を調べました。

ざっくりですが、取得した情報を元にグラフの SVG 画像を作成、そしてページに表出する方法を記載します。

  1. テンプレートに img タグを追加
  2. urls.py に URL ディスパッチャを追加
  3. views.py に 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 を返します。

処理の流れは下記の通りです。

  1. URL ディスパッチャからget_trend_svg が呼び出される
  2. YtTrendChannel モデルからデータを取り出す
  3. x と y のリストを作成
  4. make_chart 関数に x と y を渡しグラフを作成
  5. convert_to_svg 関数でグラフを SVG に変換
  6. 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 関数の中でコメントを色々書いていますが、メモリ上に一時ファイルを保存できるため、実ファイルが作られなくて済むんだそうです。

すると下記の様にチャートが表示されます。表示がだいぶ崩れてますがこれは後ほど調整するということで。。。

【Django】static ファイルの配置と設定

プロジェクト単位かアプリケーション単位かで若干ファイルの配置場所と設定方法が違いますが、まずは settings.py の INSTALLED_APPS に「'django.contrib.staticfiles'」の記述があることを確認します。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles', # この記述があることを確認

    'sample_django_app.apps.SampleDjangoAppConfig'
]

プロジェクト単位で使う static の場合

プロジェクトフォルダ直下に「static」フォルダを作成し、ファイルを配置。

settings.py の「STATICFILES_DIRS」にパスを追加します。

STATICFILES_DIRS = [
    os.path.join(BASE_DIR,'static'),
]

アプリケーション単位で使う static の場合

アプリケーションフォルダ直下に「static」フォルダを作成し、ファイルを配置。

settings.py に「STATIC_URL = '/static/'」の記述があることを確認。

STATIC_URL = '/static/'

【mac】Django プロジェクトで PostgreSQL のデータベースを利用する(仮想環境内)

Python 仮想環境内で Django プロジェクト用データベースを PostgreSQL で作成し、Django プロジェクトと紐づける方法を書きます。

今回は例として「sample_db」というデータベース作成し、プロジェクトと紐づけます。

  1. PostgreSQL で新規のデータベースを作成
  2. Django プロジェクトとデータベースの紐付け
  3. 仮想環境で接続用モジュールをインストール

PostgreSQL で新規のデータベースを作成

PostgreSQL を起動し「sample_db」という名前のデータベースを作成します。

% brew services start postgresql
% createdb sample_db

「psql -l」でデータベースのリストを表示すると、「sample_db」が作成されたのが確認できます。

% psql -l
                                         List of databases
   Name    |      Owner       | Encoding | Collate | Ctype |           Access privileges           
-----------+------------------+----------+---------+-------+---------------------------------------
 postgres  | ユーザー名         | UTF8     | C       | C     | 
 sample_db | ユーザー名         | UTF8     | C       | C     | 
 template0 | ユーザー名         | UTF8     | C       | C     | =c/ユーザー名                  +
           |                  |          |         |       | ユーザー名=CTc/ユーザー名
 template1 | ユーザー名         | UTF8     | C       | C     | =c/ユーザー名                  +
           |                  |          |         |       | ユーザー名=CTc/ユーザー名
(4 rows)

ちなみにデータベースを削除したい場合は「dropdb sample_db」と入力します。

Django プロジェクトとデータベースの紐付け

Django プロジェクトの settings.py を変更し、先ほど新規作成したデータベースを紐付けます。

プロジェクト作成時に自動作成された settings.py の内容は下記の通り。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

これを下記の様に変更します。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'sample_db',
        'USER': os.environ.get('DB_USER'),
        'PASSWORD': os.environ.get('DB_PASSWORD'),
        'HOST': '',
        'PORT': '',
    }
}

仮想環境で接続用モジュールをインストール

Python で PostgreSQL データベースに接続するために、接続用ドライバのモジュールが必要となります。もっとも多く使われている psycopg2 というモジュールをインストールします。

仮想環境に入った状態で「pip install psycopg2-binary」を実行します。

(sample_venv) % pip install psycopg2-binary
Collecting psycopg2-binary
  Downloading https://files.pythonhosted.org/packages/2c/85/c26507efb110f5a91f503e517f1db55f12ebecb001ff224b2cea234a07ef/psycopg2_binary-2.8.6-cp38-cp38-macosx_10_9_x86_64.macosx_10_9_intel.macosx_10_10_intel.macosx_10_10_x86_64.whl (1.5MB)
     |████████████████████████████████| 1.5MB 7.1MB/s 
Installing collected packages: psycopg2-binary
Successfully installed psycopg2-binary-2.8.6
WARNING: You are using pip version 19.2.3, however version 21.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
(sample_venv) % 

ちなみに

PyCharm の runserver を実行した時に下記のエラーが出て一瞬焦りましたが、PostgreSQL を起動したら治りました。

django.db.utils.OperationalError: could not connect to server: No such file or directory

ちなみのちなみに、settings.py の「DATABASES」の部分をコメントアウトなどするとデータベースを起動していなくても問題なく runserver を実行できます。

Python 仮想環境に Django プロジェクトと Django アプリケーションを作成する

  1. Django プロジェクトの作成
  2. Django アプリケーションの作成
  3. アプリケーションをプロジェクトに登録

Django プロジェクトの作成

仮想環境に入った状態で下記を実行します。今回例として仮想環境内に「Django_Project」フォルダを作ったのでその中に「sample_django_project」という名前の Django プロジェクトを作成します。

(sample_venv) % cd Django_Project
(sample_venv) Django_Project % django-admin startproject sample_django_project

すると下記の様に「Django_Project」フォルダの配下に「sample_django_project」フォルダとファイルが一式作成されます。

Django アプリケーションの作成

上の画像「manage.py」がある階層へ移動します。

(sample_venv) % cd sample_django_project

そして下記を実行して「sample_django_app」という名前で Django アプリケーションを作成します。

(sample_venv) sample_django_project % python manage.py startapp sample_django_app

すると、「sample_django_project」というプロジェクトの配下に「sample_django_app」が作成されました。

アプリケーションをプロジェクトに登録

アプリケーションフォルダの apps.py を開くと下記の通り「SampleDjangoAppConfig」というクラスがあるので、これをプロジェクトの settings.py に登録します。

from django.apps import AppConfig


class SampleDjangoAppConfig(AppConfig):
    name = 'sample_django_app'

settings.py の INSTALLED_APPS のリストに下記の通り「アプリケーション名.apps.クラス名」の形で追記します。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'sample_django_app.apps.SampleDjangoAppConfig'
]

これで Django プロジェクトと Django アプリケーションの作成は完了です。