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 関数の中でコメントを色々書いていますが、メモリ上に一時ファイルを保存できるため、実ファイルが作られなくて済むんだそうです。

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

【mac】PostgreSQL にテーブルを作って csv のデータを挿入した時のメモ

  1. テーブルの作成
  2. csv ファイルをテーブルに copy (insert)
  3. (ついでに)inspectdb でモデル情報を確認

1. テーブルの作成

ターミナルにコマンド「psql データベース名」を入力し対象のデータベースを開いて、別で書いておいた create 文のコピペで処理可能。最後に「;」をつけるのを忘れない様に気をつけてください。

下記の例では「yt_trend_channel」というテーブルを作りました。

sample_db=# create table yt_trend_channel (
channel_id varchar(30),
view_count varchar(20),
join_date varchar(20),
country varchar(20),
channel_name varchar(100),
thumbnail_url varchar(50),
subscriber_count varchar(20),
data_date varchar(8)
);

2. csv ファイルをテーブルに copy (insert)

csv ファイルを テーブルに copy します。

psql を起動する前に対象のファイルのディレクトリへ cd しておくと、単純にファイル名を渡すだけで済むのでわかりやすいです。

下記の例では先ほど create した yt_trend_channel テーブルに、別で用意した yt_trend_channel.csv という csv ファイルのデータを copy (insert) しています。

sample_db=# \copy yt_trend_channel from 'yt_trend_channel.csv' with csv
COPY 4259
sample_db=#

全部で 4259 件のレコードが copy されました。

select クエリで確認します。

sample_db=# select * from yt_trend_channel;
        channel_id        | view_count  | join_date  |    country     |                            channel_name                             |                   thumbnail_url                    | subscriber_count | data_date 
--------------------------+-------------+------------+----------------+---------------------------------------------------------------------+----------------------------------------------------+------------------+-----------
 UC9OvLVXb-okaVtoK9V8Biwg | 103499474   | 2018/05/23 | 日本           | ABEMA バラエティ【公式】                                            | https://yt3.ggpht.com/ytc/AAUvwnjBKC3y2KycvPfvHdn1 |  19.9万人        | 20210104
 UCZZ0UGjWsRdM8_5bsqtxYaQ | 190130764   | 2015/10/02 | 日本           | RIZIN FIGHTING FEDERATION                                           | https://yt3.ggpht.com/ytc/AAUvwnjDj0e-hS0um-ZKG1Tc |  35万人          | 20210104
 UCaminwG9MTO4sLYeC3s6udA | 3201321432  | 2016/03/18 | 日本           | ヒカル(Hikaru)                                                    | https://yt3.ggpht.com/ytc/AAUvwnhBYwfz6DycI4xBURsZ |  428万人         | 20210104
 UCPn_e-7LD7SbzIBxdU_XBnQ | 21113326    | 2020/07/30 | 日本           | あーたろチャンネル                                                  | https://yt3.ggpht.com/ytc/AAUvwngZDvNAYRF_qgPfOgy9 |  18.7万人        | 20210104
 UC4-TMrb7Mm4KnYx1VsUgcJA | 135489838   | 2012/08/04 | 日本           | あいり                                                              | https://yt3.ggpht.com/ytc/AAUvwngy1VncfSpEQkWmZXYK |  26.8万人        | 20210104

ちなみに MySQL の場合は下記の様な感じ

load data local infile  'yt_trend_channel.csv'
into table yt_trend_channel
fields terminated by ',' 
enclosed by '"'
;

3. (ついでに Django)inspectdb でモデル情報を確認

実はそもそも Django で使うテーブルなので「python manage.py inspectdb テーブル名」でモデル情報を確認してみます。

% python manage.py inspectdb yt_trend_channel
# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
#   * Rearrange models' order
#   * Make sure each model has one field with primary_key=True
#   * Make sure each ForeignKey has `on_delete` set to the desired behavior.
#   * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
from django.db import models


class YtTrendChannel(models.Model):
    channel_id = models.CharField(max_length=30, blank=True, null=True)
    view_count = models.CharField(max_length=20, blank=True, null=True)
    join_date = models.CharField(max_length=20, blank=True, null=True)
    country = models.CharField(max_length=20, blank=True, null=True)
    channel_name = models.CharField(max_length=100, blank=True, null=True)
    thumbnail_url = models.CharField(max_length=50, blank=True, null=True)
    subscriber_count = models.CharField(max_length=20, blank=True, null=True)
    data_date = models.CharField(max_length=8, blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'yt_trend_channel'
% 

class 文を models.py にコピーすると Django でモデルとして使える様になります。その際に 1 つ「primary_key=True」のフィールドを持つ必要がありますので確認してください。

【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 アプリケーションの作成は完了です。

Mac で Python 仮想環境に Django をインストールしたい

Python の仮想環境を作る

仮想環境を作成したいディレクトリへ行き、下記のコマンドを実行します。例として「sample_virtual_environment」という仮想環境を作ります。

 python3 -m venv sample_virtual_environment

すると下記の様に Python の実行に必要なファイルやフォルダが一式作成されます。

これで仮想環境の作成は完了です。実際に仮想環境「sample_virtual_environment」を起動するには下記を実行します。

% cd sample_virtual_environment/bin
% source activate

下記の様にカッコの中に仮想環境の名前が表示されると思います。

(sample_virtual_environment) %

仮想環境から出たい場合は「deactivate」を実行します。

(sample_virtual_environment) % deactivate
%

Django をインストールする

仮想環境で Django をインストールするには、仮想環境を activate した状態で下記を実行します。

(sample_virtual_environment) % pip install django

下記のようなログが表示されて最後の方に「Successfully installed」と出ていれば成功です。

(sample_virtual_environment) % pip install django
Collecting django
  Downloading https://files.pythonhosted.org/packages/b8/6f/9a4415cc4fe9228e26ea53cf2005961799b2abb8da0411e519fdb74754fa/Django-3.1.7-py3-none-any.whl (7.8MB)
     |████████████████████████████████| 7.8MB 337kB/s 
Collecting asgiref<4,>=3.2.10 (from django)
  Downloading https://files.pythonhosted.org/packages/89/49/5531992efc62f9c6d08a7199dc31176c8c60f7b2548c6ef245f96f29d0d9/asgiref-3.3.1-py3-none-any.whl
Collecting sqlparse>=0.2.2 (from django)
  Downloading https://files.pythonhosted.org/packages/14/05/6e8eb62ca685b10e34051a80d7ea94b7137369d8c0be5c3b9d9b6e3f5dae/sqlparse-0.4.1-py3-none-any.whl (42kB)
     |████████████████████████████████| 51kB 9.6MB/s 
Collecting pytz (from django)
  Downloading https://files.pythonhosted.org/packages/70/94/784178ca5dd892a98f113cdd923372024dc04b8d40abe77ca76b5fb90ca6/pytz-2021.1-py2.py3-none-any.whl (510kB)
     |████████████████████████████████| 512kB 8.1MB/s 
Installing collected packages: asgiref, sqlparse, pytz, django
Successfully installed asgiref-3.3.1 django-3.1.7 pytz-2021.1 sqlparse-0.4.1
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_virtual_environment) %

以上で Django のインストールは完了です。続けて Django プロジェクトそして Django アプリケーションの作成はこちらの記事へ。