LoginSignup
147
169

More than 3 years have passed since last update.

Django REST framework with Vue.js

Last updated at Posted at 2017-01-29

はじめに

Django REST frameworkVue.jsが気になったので、組み合わせて動かしてみた。

構成・動作概要

Djangoのhtmlのテンプレート機能は使わずに、静的ファイルとしてHTMLデータを返却し、ブラウザからVue.jsでRestAPI経由でデータを取得した情報の一覧を表示してみる。
(DBはSQLite3、サンプルデータは自分の記事のストック数を使った。)

vuesとrest.png

動作環境

PythonはPython 3.5.1
パッケージは以下

# pip3 freeze
Django==1.10.5
django-filter==1.0.1
djangorestframework==3.5.3

Vue.jsとaxiosはCDNで取ってきていて、投稿時点(2017/1/30)のバージョンは以下。

  • Vue.js: 2.1.10
  • axios: 0.15.3

試す

とりあえずDjangoプロジェクト作成

いつもの。

# django-admin startproject qiitalist
# cd qiitalist/
# python manage.py startapp stocks

完成形

こんな感じの構成になった。①~⑥個別に実装を見ていきます。(▲=今回使わないファイル)

qiitalist
│  fixture.json # ②モデル追加
│  manage.py
│
├─qiitalist
│  │  settings.py # ①設定追加
│  │  urls.py # ⑤URL設定追加
│  │  wsgi.py # ▲
│  │  __init__.py
│  │
│  └─static # ⑥静的ファイル作成
│          vue_grid.css
│          vue_grid.html
│          vue_grid.js
│
└─stocks
    │  admin.py # ▲
    │  apps.py # ▲
    │  models.py # ②モデル追加
    │  tests.py # ▲
    │  serializer.py ③シリアライザ追加
    │  urls.py  # ⑤URL設定追加
    │  views.py # ④View追加
    │  __init__.py
    │
    └─migrations

①設定追加

実装するアプリと、REST Frameworkの設定を追加。

settings.py
# 追加分だけ抜粋
# アプリの追加
INSTALLED_APPS += [
    'stocks',
    'rest_framework',
]
# REST API設定(フィルタ、ページング)
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',),
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 5
}
# views.pyで使うパスを定義
PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__))
STATICFILES_DIRS = (os.path.join(PACKAGE_ROOT, 'static'),)

②モデル追加

REST APIで公開するデータモデル、及び初期データ(フィクスチャ)を準備。

models.py
from django.db import models


class Stock(models.Model):

    class Meta:
        db_table = "stock"
    id = models.AutoField(primary_key=True)
    title = models.TextField()
    stock_count = models.IntegerField()
fixture.json
[
  {
    "model": "stocks.stock",
    "pk": 1,
    "fields": {
      "title": "Pythonで機械学習はじめました(Qiitaへの投稿もはじめました) データ準備編",
      "stock_count": 29
    }
  },
  {
        "model": "stocks.stock",
        "pk": 2,
        "fields": {
          "title": "ScrapyとDjangoでラーメンマップ作成",
          "stock_count": 23
     }
  },
  {
      "model": "stocks.stock",
      "pk": 3,
      "fields": {
        "title": "Pythonで機械学習はじめました データ前処理編",
        "stock_count": 22
     }
  },
  {
       "model": "stocks.stock",
       "pk": 4,
       "fields": {
         "title": "PythonでRabbitMQメッセージの通知アプリ with Growl ~ラズパイとJuliusを添えて~",
         "stock_count": 3
     }
   }
]

③シリアライザ追加

モデルをシリアライズするSerialzierの定義を追加。

serializers.py
from rest_framework import serializers
from stocks.models import Stock


class StockSerializer(serializers.ModelSerializer):

    class Meta:
        model = Stock
        fields = ("id", "title", 'stock_count')

④View追加

Viewの定義を追加。

views.py
import os

from django.conf import settings
from django.http.response import HttpResponse
from rest_framework import viewsets

from stocks.models import Stock
from stocks.serializer import StockSerializer

# 静的ファイルを返すView
def index(_):
    # render等でDjangoのtemplateとして処理すると「{{}}」がVue.jsに渡る前消えてしまう。
    # 良い解決方法が浮かばなかったので、static配下に置いたファイルをopenして投げることで回避。
    html = open(
        os.path.join(settings.STATICFILES_DIRS[0], "vue_grid.html")).read()
    return HttpResponse(html)

# RestAPIのviewsets
class StockViewSet(viewsets.ModelViewSet):
    queryset = Stock.objects.all()
    serializer_class = StockSerializer
    # APIのフィルタで使えるフィールドを指定
    filter_fields = ("id", "title", 'stock_count')

⑤URLの設定追加

親(qiitalist)と子(stocks)で一応分割。

urls.py(qiitalist)
from django.conf.urls import include, url
from stocks.urls import urlpatterns as qiitalist_url

urlpatterns = [
    url(r'^qiita/', include(qiitalist_url)),
]

urls.py(stocks)
from django.conf.urls import include, url
from rest_framework import routers

from stocks.views import StockViewSet, index

router = routers.DefaultRouter()
router.register(r'stock', StockViewSet)

urlpatterns = [
    # qiita/api/stock/
    url(r'api/', include(router.urls)),
    # qiita/
    url(r'', index, name='index'),
]

⑥静的ファイル作成

https://jp.vuejs.org/v2/examples/grid-component.htmlのグリッドコンポーネントの例を真似します。

vue_grid.css
/* コピペなので割愛 */
vue_grid.js
//上部のコンポーネント実装部分はコピペなので割愛
//new Vue部分だけRestAPIから指定のデータを取得するように改造
var demo = new Vue({
      el: '#demo',
      data: {
        searchQuery: '',
        gridColumns: ["id", "title", 'stock_count'], // 変更
        gridData: [] // データはAPIで取ってくるので削除
      },
      created: function(){ //RestAPIから取ってきてgridDataに追加する。
          var self = this //スコープ的に必要っぽい(this.gridData.pushではエラーになる。)
          axios.get('/qiita/api/stock/')
            .then(function(response){
              for(var i = 0; i < response.data.results.length; i++){
                      self.gridData.push(response.data.results[i]);
                  }
            });
          }
    })
vue_grid.html
<!-- vue_grid.htmlは表示項目に合わせてちょっと改造。 -->
<!DOCTYPE html>
<html>
<head>
  <title>Welcome to Vue</title>
  <!-- CSS読み込み -->
  <link rel="stylesheet" type="text/css" href="/static/vue_grid.css">

  <!-- JS(CDN)読み込み -->
  <!-- Vue.js -->
  <script src="https://unpkg.com/vue/dist/vue.js"></script>
  <!-- Axios(vue-resourceの代わり) -->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<script type="text/x-template" id="grid-template">
  <table>
    <thead>
      <tr>
        <th v-for="key in columns"
          @click="sortBy(key)"
          :class="{ active: sortKey == key }">
          {{ key | capitalize }}
          <span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
          </span>
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="entry in filteredData">
        <td v-for="key in columns">
          {{entry[key]}}
        </td>
      </tr>
    </tbody>
  </table>
</script>

<div id="demo">
  <form id="search">
    Search <input name="query" v-model="searchQuery">
  </form>
  <demo-grid
    :data="gridData"
    :columns="gridColumns"
    :filter-key="searchQuery">
  </demo-grid>
</div>
</body>

<!-- JS読み込み -->
<script src="/static/vue_grid.js"></script>
</html>

動かしてみる。

DBを作成。

# python manage.py makemigrations
# python manage.py migrate

初期データを投入。

# python manage.py loaddata fixture.json

サーバを起動。

# python manage.py runserver

ブラウザからAPIコンソールhttp://localhost:8000/qiita/api/にアクセス。

apitop.png

http://localhost:8000/qiita/api/stock/でstockの一覧取得。
(①のページング設定で最大5件まで取得して表示 ※注 登録したStockは4件なので特に意味は無し)

apistock.png

http://localhost:8000/qiita/にアクセスして表示してみる。

qiitatop.png

所感

とりあえず動く所までは作れたので、勉強する意欲が沸き始めた。
Vue.jsはほぼ写経だけど、AngularJS、React.jsよりも使いやすい(直感的)気がした。
扱うデータが件数少ないのが致命的。面白くないのでどんどん記事を増やそう…。

参考

147
169
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
147
169