[PYTHON] Django REST framework with Vue.js

</ i> Introduction

I was curious about ** Django REST framework ** and ** Vue.js **, so I tried to combine them.

</ i> Configuration / operation overview

Instead of using Django's html template function, return the HTML data as a static file and display a list of information obtained from the browser via Rest API with Vue.js. (DB used SQLite3, sample data used the number of stocks of my article.)

vuesとrest.png

</ i> Operating environment

Python is ** Python 3.5.1 ** The package is below

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

Vue.js and axios are fetched by CDN, and the version at the time of posting (2017/1/30) is as follows.

  • Vue.js: 2.1.10
  • axios: 0.15.3

</ i> Try

Create a Django project for the time being

The usual.

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

Completed form

The composition looks like this. (1)-(6) We will look at the implementation individually. (▲ = File not used this time)

qiitalist

│ fixture.json # ② Model added │ manage.py │ ├─qiitalist │ │ settings.py # ① Add settings │ │ urls.py # ⑤ URL setting added │ │ wsgi.py # ▲ │ │ init.py │ │ │ └─ static # ⑥ Static file creation │ vue_grid.css │ vue_grid.html │ vue_grid.js │ └─stocks │ admin.py # ▲ │ apps.py # ▲ │ models.py # ② Add model │ tests.py # ▲ │ serializer.py ③ Add serializer │ urls.py # ⑤ URL setting added │ views.py # ④ View added │ init.py │ └─migrations

① Add settings

Added the application to be implemented and the setting of REST Framework.

settings.py


#Excerpt only for the addition
#Add app
INSTALLED_APPS += [
    'stocks',
    'rest_framework',
]
#REST API settings (filters, paging)
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',),
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 5
}
# views.Define the path used by py
PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__))
STATICFILES_DIRS = (os.path.join(PACKAGE_ROOT, 'static'),)

② Model addition

Prepare the data model to be published by REST API and the initial data (fixture).

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": "I started machine learning with Python(I also started posting to Qiita)Data preparation",
      "stock_count": 29
    }
  },
  {
        "model": "stocks.stock",
        "pk": 2,
        "fields": {
          "title": "Ramen map creation with Scrapy and Django",
          "stock_count": 23
     }
  },
  {
      "model": "stocks.stock",
      "pk": 3,
      "fields": {
        "title": "I started machine learning with Python Data preprocessing",
        "stock_count": 22
     }
  },
  {
       "model": "stocks.stock",
       "pk": 4,
       "fields": {
         "title": "RabbitMQ message notification app in Python with Growl ~ with Raspberry Pi and Julius ~",
         "stock_count": 3
     }
   }
]

③ Add serializer

Added the definition of Serialzier to serialize the model.

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')

④ Add View

Added View definition.

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 that returns a static file
def index(_):
    #If you process it as a Django template with render etc., "{{}}Is Vue.It disappears before passing to js.
    #I couldn't find a good solution, so I avoided it by opening and throwing the file under static.
    html = open(
        os.path.join(settings.STATICFILES_DIRS[0], "vue_grid.html")).read()
    return HttpResponse(html)

#Rest API viewssets
class StockViewSet(viewsets.ModelViewSet):
    queryset = Stock.objects.all()
    serializer_class = StockSerializer
    #Specify fields that can be used in API filters
    filter_fields = ("id", "title", 'stock_count')

⑤ Add URL settings

Divided by parent (qiitalist) and child (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'),
]

⑥ Static file creation

Imitate the grid component example of https://jp.vuejs.org/v2/examples/grid-component.html </ font>.

vue_grid.css


/*Omitted because it is a copy*/

vue_grid.js


//The upper component mounting part is a copy, so it is omitted.
//Modified to get the specified data from Rest API only for the new Vue part
var demo = new Vue({
      el: '#demo',
      data: {
        searchQuery: '',
        gridColumns: ["id", "title", 'stock_count'], //Change
        gridData: [] //Data is fetched by API, so delete it
      },
      created: function(){ //Take it from the Rest API and add it to gridData.
          var self = this //It seems to be necessary in terms of scope (this.gridData.An error will occur with 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.The html is slightly modified according to the display items.-->
<!DOCTYPE html>
<html>
<head>
  <title>Welcome to Vue</title>
  <!--CSS loading-->
  <link rel="stylesheet" type="text/css" href="/static/vue_grid.css">

  <!-- JS(CDN)Read-->
  <!-- Vue.js -->
  <script src="https://unpkg.com/vue/dist/vue.js"></script>
  <!-- Axios(vue-Instead of 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 read-->
<script src="/static/vue_grid.js"></script>
</html>

</ i> Try moving it.

Create a DB.

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

Input initial data.

# python manage.py loaddata fixture.json

Start the server.

# python manage.py runserver

Access the API console http: // localhost: 8000 / qiita / api / from your browser.

apitop.png

Get a list of stocks at http: // localhost: 8000 / qiita / api / stock /. (Up to 5 items can be acquired and displayed in the paging setting of ①. * Note: There is no particular meaning because the registered stock is 4 items.)

apistock.png

Access http: // localhost: 8000 / qiita / and try to display it.

qiitatop.png

</ i> Impressions

For the time being, I was able to make a place where I could move, so I started to get motivated to study. Vue.js is almost a sutra copy, but I felt it was easier to use (intuitive) than AngularJS and React.js. It is fatal that the number of data handled is small. It's not interesting, so let's increase the number of articles ...

</ i> Reference

-Implement API at explosive speed using Django REST Framework -Examples of grid components -Generate data using Django's Fixture -Axios is recommended when doing Ajax with Vue. -A JavaScript framework that makes it easy to start with Vue.js

Recommended Posts