東京の気象データをDRF経由でVueで作った画面に表示させるまで(概要)

今回は趣味で、もとい、検証のため作ってみたWebアプリのことを書きたいと思います。 やりたかったことは、「データベースからWebAPI経由でデータを取得して画面に表示する」、ただそれだけです。 まぁ、これぐらいできれば大概何でもできるでしょう。

似たようなことをやりたい方へのヒントになればと思い、情報をまとめています。細かな手順やコードは記事には書いていません。 参照先のサイトを確認すればできると思います。

作成の際に気を付けたのは以下のような点です。

  • フレームワークらしく、モジュール化・コンポーネント化が考慮された納得できるファイル構成であること。1ファイルに異なる処理をダラダラ書かないこと。
  • 機械的に生成できる部分は省力化すること。(手で書くと間違えるので)

画面の見た目

こんな感じです。(しょぼいですが)

f:id:handatec:20200422134817p:plain
サンプルアプリ画面
こちらはグラフ。気温のデータをvue-chart.jsで表示させています。
f:id:handatec:20200428192341p:plain
サンプルアプリ画面(グラフ)

環境

以下のような構成で作成しています。

フロントエンド(Vue)

  • Vue CLI 4.3.0
  • Vuetify 2.2.11
  • axios 0.19.2
  • vue-router 3.1.6
  • vue-chartjs 3.5.0
  • chart.js 2.9.3

バックエンド(Django

データベースサーバ(PostgreSQL

OS、サーバ構成、ネットワーク(共通)

OSはいずれもUtuntu 18.04 LTS、VirtualBox仮想マシン3台構成で同一ネットワーク上で動作させています。

フロントエンドにVue.jsを選んだ理由

単に「速かったから」です。ReactやAngularはそれ自体のインストールや開発サーバの起動に結構な時間がかかったり、エラーが出たりしてテンポよく作業できないと感じました。また、マイクロサービスアーキテクチャを念頭に置くと、フロントエンドはとにかくUI、表示に集中できればよいという考えがあり、Vueはそれにマッチしました。コンポーネント指向も気に入りました。

バックエンドにDjango/DRFを選んだ理由

バックエンドは少し複雑な処理を行って、結果をWebAPI経由でフロントに渡すところまでができれば何でもよかったのです。オールJavascriptで揃えたかったのですが、Python系にしたのは昨今の機械学習/ディープラーニングやデータ分析の主流になっていて、それらと組み合わせる場合に都合がよさそうという理由です。

Python系Webフレームワークの2大有名どころとして、DjangoとFlaskがあると思います。DjangoDjango Rest Frameworkと合わせてデータベースアクセスからAPI作成までフルスタックであるところ、また、DBマイグレーション機能がついているところが気に入りました。FlaskはAPI作成は簡単ですが、データベースアクセスとデータのJSON化に他のライブラリ(SQLAlchemyやmarshmallow)を組み合わせる必要があり、正直面倒だと感じました。

よさそうな書籍

ここで、よさそうな書籍をメモしておきます。Vue、Django Rest Frameworkそれぞれ1冊づつです。本来であれば、本屋さんに行って中身をざっとチェックしてから購入したいのですが、昨今のコロナ対応の件で本屋さん閉まっててできない状況です。

両書籍とも電子版がありますが、自分は今のところサンプルをダウンロードしているだけで購入の踏ん切りがついていません(笑)。ただ、DRFの方はサンプルでも1章まるまる読むことが可能で、概要が理解できたので内容に価値があると思いました。

サンプルアプリの元ネタ

今回のアプリ作成にあたり、以下のサイトを参考、ヒントにさせていただきました。大筋は同じですが、サーバOSやデータベースなどの一部の環境、フロントエンドの方式、扱うデータ、modelsの記述のやり方(後述)などが異なります。 qiita.com

データベース周りのポイント

アプリで扱うデータとデータベース、テーブルの準備

ある地点の気温、湿度、気圧のデータを使おうと考えました。気象庁のサイトではAPIなどは公開されていないため、「過去の気象データ・ダウンロード」より手作業でCSVファイルを取得して多少加工し、PostgreSQLにテーブルを作成してpgAdmin4のインポート機能で突っ込みました。テーブルの作成はDDLを書くのが面倒なので、GUIから行いました。 CSVの加工はtalendなどETLツールを使っても面白いかもしれないですね。

気象庁|過去の気象データ・ダウンロード

Djangoのmodelsの記述をテーブル定義から生成

Djangoのmodelsを手で書くのが面倒なので、inspectdbを使用してテーブル定義からリバースで生成しました。import文以外は生成したままでいけますし、makemigrations、migrateを実行しても問題起こりません。素晴らしい。

こちらのサイトを参考にさせて頂きました。 qiita.com

あと、地味に面倒なのは、DRFRest APIを作成する場合、models, serializers, viewsの3点セットを作成する必要があるところです。

psycopg2インストールでハマった

PythonアプリからPostgreSQLへ接続する際に必要となる「psycopg2」のインストールでハマりました。何をやってもビルドエラーが出まくるので、暫定的にバイナリ版をインストールしました。

sudo python3 -m pip install psycopg2-binary

このインストール方法は公式にも書かれていますが、sslモジュールと競合したりアップグレードの時に困るだろうからProduct版はソースからビルドしろ(?)云々の警告、ノートの記載があります。

Installation — Psycopg 2.8.5 documentation

メモ:バックエンド側の環境構築について感じたこと

anacondaも使ってみましたが、結局condaでインストールできないパッケージがあり、じゃあ最初からpyenvやpip使っておけばいいじゃんと思ってしまいました。メリットは、シェル上で使用している仮想環境の名前が表示されていて分かりやすいところぐらいでしょうか。Jupyter notebookとか手軽に使いたい人向けですかね。

環境管理の点では以下のサイトの記事が興味深かったです。 watlab-blog.com www.zopfco.de

CORS、プリフライト対応のポイント

Vueでのaxiosの使い方と合わせてどハマりしましたが、解決しました。詳細は下記の別記事をご覧ください。 handatec.hatenablog.com

フロントエンド側のポイント

Vuetifyの使い方

画面の見た目はVuetifyで作成しています。以下のサイトと公式ドキュメントを参考にさせて頂きました。 reffect.co.jp

vue-routerについて

以下のサイトが非常に分かりやすかったです。ルーティングに関する記載はrouter.jsに集中させることにします。

https://b1tblog.com/2019/10/03/vue-router/b1tblog.com

router.jsの記載例は以下の通りです。

import Vue from 'vue'
import Router from 'vue-router'

import table from './components/table'
import chart from './components/chart'

Vue.use(Router)

export default new Router({
  mode: 'history',
  routes: [
    {
        //一覧表用
        path: '/table',
        component: table,
        name: 'table',
    },
    {
        //グラフ用
        path: '/chart',
        component: chart,
        name: 'chart',
    },
  ]
})

Vuetifyのナビゲーション(画面左のメニュー)からのリンク設定は参考にしたサイトはありますが、少々込み入っているのでポイントを書いておきます。

上記router.jsを記載したうえで、App.vue内の3か所に記載を追加します。

  • コンテンツの表示領域の記載。タグ<router-view />部分のコンテンツがルーティング設定に基づいて切り替わります。
<v-content>
    <router-view />
</v-content>
  • <v-navigation-drawer>タグ内のリンク設定。:to="list.link"がリンクの記載になります。
<v-list-item
    v-for="list in nav_list.lists"
    :key="list.name"
    :to="list.link"
>
    <v-list-item-title>
        {{ list.name }}
    </v-list-item-title>
</v-list-item>
  • 具体的なリンク先について、メニューの設定を行っている箇所にlink;xxxを追記。xxx部分はrouter.jsに記載したpathと一致させます。
export default {
  data(){
    return{
      drawer: null,
      nav_lists:[
        {
          name:'ダッシュボード',
        },
        {
          name:'気象データ',
          lists:[
            {
              name:'一覧',
              link:'/table'
            },
            {
              name:'グラフ',
              link:'/chart'
            },
          ]
        },
      ],
    }
  },

現時点で やり残していること

  • グラフのデータ取得と表示のタイミング合わせ
  • グラフのコンテナvueファイル作成
  • API認証(アプリケーションIDとか発行して認証したい)
  • (Restじゃない)WebAPIの作成

 今回はここまで