LoginSignup
49
50

More than 3 years have passed since last update.

フロントNuxt.js - バックDjango間のAPI連携を実装してみた

Posted at

初めに

初投稿です。
最近Web系の開発に興味があり、色々勉強しています。
この記事では、バックエンドの部分をDjangoで、
フロントエンドの部分をNuxt.jsで作成したものについて、
とりあえず開発環境で動くもので書いていきます。

準備

下記イメージを元にdocker-composeを使ってそれぞれの環境を構築
qiita用1 (2).jpg

環境

Window10 Pro
Docker desktop v2.1.0.5
Python 3.6.9
django 2.2.7
djangorestframework 3.10.3
MySQL Ver 8.0.17
Nuxt.js v2.11.0

ディレクトリ構造

.
├─django
│  ├─manage.py
│  ├─composeexample
│  │   ├─settings.py
│  │   ├─urls.py
│  │   ├─wsgi.py
│  │   └─__init__.py
│  └─myapp
│      ├─migrations
│      ├─admin.py
│      ├─apps.py
│      ├─models.py
│      ├─renderers.py
│      ├─serializers.py
│      ├─tests.py
│      ├─urls.py
│      ├─views.py
│      └─__init__.py
│
├─docker-compose.yml
│
├─dockerfiles
│  ├─django_docker
│  │   ├─dockerfile
│  │   └─requirements.txt
│  └─nuxt_docker
│      └─dockerfile
│
├─mysql
│  └─conf.d
│
└─nuxt
    └─front
       └─以下略

各種ファイル

./docker-compose.yml
version: '3'

services:
  db:
    image: mysql:latest
    restart: always
    container_name: mysql
    environment:
      MYSQL_ROOT_PASSWORD: test
      MYSQL_USER: test
      MYSQL_DATABASE: test
      MYSQL_PASSWORD: test
    ports:
      - 3306:3306
    expose:
      - 3306
    volumes:
      - mysqldata:/var/lib/mysql
      - ./mysql/conf.d:/etc/mysql/conf.d

  web:
    container_name: django
    build: ./dockerfiles/django_docker
    command: 
      python3 manage.py runserver 0.0.0.0:8000
    volumes:
      - ./django:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

  front:
    container_name: nuxt
    build: ./dockerfiles/nuxt_docker 
    tty: true
    volumes:
      - ./nuxt:/code
    ports:
      - "3000:3000"

volumes:
  mysqldata:
./dockerfiles/django_docker/dockerfile
# ベースイメージを指定
FROM python:3.6-stretch

ENV PYTHONUNBUFFERED 1
RUN mkdir /code

# ディレクトリを移動する
WORKDIR /code

# pipでrequirements.txtに記載のパッケージをインストール
COPY requirements.txt /code/
RUN pip3 install -r requirements.txt

COPY . /code/

※djangoのイメージはOpen SSHの鍵長の関係でstretchでbuildしてます。こちら参照

./dockerfiles/django_docker/requirement.txt
Django>=2.0,<3.0
djangorestframework
django-webpack-loader
django-cors-headers
mysqlclient

※requierment.txtに必要となるpythonのパッケージを記載します。
 Djangoだけでなく、今回APIに必要なパッケージも入れます。

 以下はDjango REST frameworkに必要なもの
 ・djangorestframework
 ・django-webpack-loader
 ・django-cors-headers

 また以下はMySQLをDjangoから使うのに必要なもの
 ・mysqlclient

./dockerfiles/nuxt_docker/dockerfile
# ベースイメージを指定
FROM node:latest

RUN mkdir -p /code

# node.js の環境変数を定義する
# 本番環境では production
ENV NODE_ENV=development

# yarnとaxiosをinstall
RUN yarn install
RUN yarn add @nuxtjs/axios

# ディレクトリを移動する
WORKDIR /code

# ポート3000番を開放する
EXPOSE 3000

djangoの構築

プロジェクトの作成

こちらの記事を参考にしてます。
Djangoを最速でマスターする part1
DockerでDjangoの開発環境を作成する(Quickstart: Compose and DjangoのMySQL版)
【Django】アプリケーションの追加手順
まずdocker-composeでコンテナを立ち上げます。MySQLから建てます。

> docker-compose up -d db

docker ps で確認

> docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES
285cf6b8473b        mysql:latest        "docker-entrypoint.s…"   2 minutes ago       Up 4 seconds        0.0.0.0:3306->3306/tcp, 33060/tcp   mysql

続いてdocker-composeでコンテナを立ち上げます。初回なのでbuildもしておきます。

> docker-compose up -d --build

Djangoのプロジェクトを作ります。(プロジェクト名は任意です)

> docker-compose run web django-admin.py startproject composeexample .

この部分が生成されます。

.
└─django
   ├─manage.py
   └─composeexample
       ├─settings.py
       ├─urls.py
       ├─wsgi.py
       └─__init__.py

続いてsetting.pyを編集します。
デフォルトではSQLiteを使用するようになっているので、これをMySQLに変更します。

#<変更前>
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

#上記を↓↓↓に変更
#ユーザ名やパスは適宜変更してください

#<変更後>
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'test',
        'USER': 'test',
        'PASSWORD': 'test',
        'HOST': 'db',   
        'PORT': '3306',
    }
}

次は連携したDBのマイグレーションを行います。

> docker-compose run web ./manage.py migrate

スーパユーザを作成します。

> docker-compose run web ./manage.py createsuperuser --username admin --email admin@localhost
Starting mysql ... done                                                                                   
Password: #パスワードを聞かれるので入力
Password (again): #再度入力
The password is too similar to the username.
This password is too short. It must contain at least 8 characters.
This password is too common. #パスワードが短いと警告が出る
Bypass password validation and create user anyway? [y/N]: y #yを入力
Superuser created successfully.

ここで一度djangoの開発用サーバを起動してみます。
docker-compose up -dでdocker-compose.ymlに記載したコンテナが全てバックグラウンドで起動します。
(Nuxtのコンテナもこの段階で起動しています)

> docker-compose up -d

> docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                         NAMES
746b29edf07b        working_web         "python3 manage.py r…"   30 hours ago        Up 30 hours         0.0.0.0:8000->8000/tcp                        django
0ae9f6506532        mysql:latest        "docker-entrypoint.s…"   30 hours ago        Up 30 hours         3306/tcp, 33060/tcp, 0.0.0.0:3306->3306/tcp   mysql
0107b2a7301b        working_front       "docker-entrypoint.s…"   30 hours ago        Up 30 hours         0.0.0.0:3000->3000/tcp                        nuxt

django開発用サーバはポート8000で待ち受けているので、
http://localhost:8000/admin にアクセスしてみます。
./docker-compose.ymlの下記に該当します。

./docker-compose.yml
#~略~
    command: 
      python3 manage.py runserver 0.0.0.0:8000
#~略~

下記画面が出れば成功です。(Chromeの拡張入れているので背景が黒いです)
image.png

作成したスーパユーザでログインしてみます。
デフォルトでは2つのユーザが作られていることがわかります。
image.png

アプリの作成

続いてアプリ部分を生成します。(アプリ名は任意です)

> docker-compose run web ./manage.py startapp myapp

この部分が生成されます。

.
└─django
   └─myapp
       ├─migrations
       ├─admin.py
       ├─apps.py
       ├─model.py
       ├─tests.py
       ├─view.py
       └─__init__.py

setting.pyに作成したアプリを追記します。
これをしないとdjango上で生成したアプリが認識されません。

./django/composeexample/setting.py
# ~略~
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp', #追加
]
# ~略~

生成されたmodels.pyにモデルを追記していきます。
とりあえず簡易なPerson_dataモデルを作成します。
人の名前と年齢のみを保持したものです。
ここに記載し、マイグレーションをすることでMySQL上にテーブルが作成されます。

./django/myapp/models.py
from django.db import models

# Create your models here.
class Person_data(models.Model):
    person_name = models.TextField()
    person_age = models.IntegerField()

下記でマイグレーションを行います。
アプリを追加した場合はmakemigrationsが必要です。

> docker-compose run web ./manage.py makemigrations
> docker-compose run web ./manage.py migrate

admin.pyに下記を追記することで、管理画面から作成したモデルを確認できます。

./django/myapp/admin.py
from django.contrib import admin
from .models import Person_data #作成したモデルをimport

# Register your models here.
admin.site.register(Person_data) #作成したモデルを追記

MYAPPとPerson_datasが追加されています。
image.png

+ADDをクリックするとデータ追加画面が開きます。
SAVEで追加できます。
image.png

適当にデータを追加していくと下記のように行が増えていきます。
image.png

下記でMySQL上でも確認できます。

> docker exec -it mysql /bin/bash #MySQLコンテナに入る

> mysql -u test -p #MySQL起動
Enter password: #本記事の例だとtestでログイン可能

> use test;
> select * from myapp_person_data;
+----+-------------+------------+
| id | person_name | person_age |
+----+-------------+------------+
|  1 | takashi     |         26 |
|  2 | naoto       |         32 |
|  3 | tomoko      |         15 |
+----+-------------+------------+
3 rows in set (0.00 sec)

ここまででdjangoの準備は完了です。

Django REST frameworkの構築

Djangoの準備ができたところで、次はDjango側にAPIを作っていきます。
GETで上記テーブルの中身を取得するだけの簡易なものを作成します。
まずはsetting.pyに追記していきます。

ここはこちらを参考にしています。
Django REST frameworkでAPIを作ってみた
Django REST Framework で API サーバーを実装して得た知見まとめ(OAuthもあるよ)

./django/composeexample/setting.py
# ~略~
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp', 
    'rest_framework', #追加
]
# ~略~

'rest_framework'を追記することで、Django REST frameworkの機能を使用できるようになります。
続いて、Serializer.pyとRenderer.pyとurls.py(myapp配下)を手動で新規作成し、下記内容を追記します。

.
└─django
   └─myapp
       ├─migrations
       ├─admin.py
       ├─apps.py
       ├─model.py
       ├─tests.py
       ├─view.py
       ├─renderers.py #これ
       ├─serializers.py #これ
       ├─urls.py #これ
       └─__init__.py
./django/myapp/serializers.py
from rest_framework import serializers
from .models import Person_data

class PersonSerializer(serializers.ModelSerializer):

    class Meta:
        model = Person_data
        fields = '__all__' 
./django/myapp/renderers.py
import json
from rest_framework.renderers import JSONRenderer

class PersonJSONRenderer(JSONRenderer):
    charset = 'utf-8'  

    def render(self, data, accepted_media_type=None, renderer_context=None):
        return json.dumps({'person_data': data},ensure_ascii=False)
./django/myapp/urls.py
from django.urls import path
from .views import PersonListApiView


urlpatterns = [
    path('get_person/', PersonListApiView.as_view()),
]

composeexample配下のurls.pyを編集し、myapp配下のurls.pyを有効にします。

./django/composeexample/urls.py
"""composeexample URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include # includeを追加

from myapp import urls # myapp配下のurls.pyをimport


urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include(urls), # 追加
]

view.pyを編集します。

./django/myapp/views.py
from django.shortcuts import render
from django.views.generic import ListView

from myapp.models import Person_data

from rest_framework import status
from rest_framework.generics import ListAPIView
from rest_framework.permissions import AllowAny

from .renderers import PersonJSONRenderer
from .serializers import PersonSerializer

# Create your views here.

class PersonListApiView(ListAPIView):
    model = Person_data # モデルを指定
    queryset = Person_data.objects.all()
    permission_classes = (AllowAny, )
    renderer_classes = (PersonJSONRenderer, ) # Rendererを指定
    serializer_class = PersonSerializer # Serializerを指定

settings.pyに下記を追記します。

./django/composeexample/settings.py
REST_FRAMEWORK = {
    # Use Django's standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ]
}

http://localhost:8000/api/get_person/ を叩いてみます。
結果をjsonで取得てきていれば成功です。
image.png

Nuxt.jsからAPIの結果を取得する

django側のAPIができたので、フロント側を作成していきます。
こちら参考にしてます。
Nuxt.js・Vue.js の基本的な使い方を理解する
Nuxt.js公式サイト

まずnuxt.jsのコンテナに入り、プロジェクトを作成します(プロジェクト名は任意です)。

yarn create nuxt-app frontを入力後の選択肢は基本全てデフォルト値を入れていますが、
rendering modeだけSingle Page Appを指定しました。
ここでの設定はあとでnuxt.config.jsというファイルから変更可能です。

> docker exec -it nuxt /bin/bash

> yarn create nuxt-app front

? Project name front
? Project description My doozie Nuxt.js project
? Author name
? Choose the package manager Yarn
? Choose UI framework None
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Choose linting tools (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Choose test framework None
? Choose rendering mode Single Page App
? Choose development tools (Press <space> to select, <a> to toggle all, <i> to invert selection)

下記は表示されれば完了です。

🎉  Successfully created project front

  To get started:

        cd front
        yarn dev

  To build & start for production:

        cd front
        yarn build
        yarn start

Done in 167.32s.

確認のために開発用サーバを動かす前に、nuxt.config.jsに追記します。
docker上でnuxt.jsを動かす際は下記が必要です。
こちら参考

./nuxt/front/nuxt.config.js
export default {
  mode: 'spa',

  //ここから
  server: {
    port: 3000,
    host: '0.0.0.0',  
  },
  //ここまでを追記

  /*
  ** Headers of the page
  */

開発用サーバを動かしてみます。
完了後のメッセージにも記載されていますが、コンテナ内で作成したプロジェクトのフォルダに移動して、
yarn devです。

> cd front
> yarn dev

http://localhost:3000/ にアクセスしてみます。
下記表示であれば成功です。
image.png

ここから編集していく際にホットリロードを有効にします。
docker上で開発するためには明示的に指定が必要です。
こちら参考
下記をファイル末尾に追記します。

./nuxt/front/nuxt.config.js
export default {
//~略~
  }, //追記の際はこのカンマを忘れずに
  watchers: {
    webpack: {
      poll: true
    }
  }
}

続いてaxiosの設定を追記していきます。
axiosはnode.jsで動くHTTPクライアントで、これを利用してnuxt.jsから先ほどのAPIのURLを叩きます。
前述のnuxtのdockerfileに記載してあるので、すでにコンテナにはインストール済です。
modulesに下記を追記、及びaxiosを追加します。
axios

./nuxt/front/nuxt.config.js
export default {
//~略~
  modules: [
    '@nuxtjs/axios',
  ],
  axios: {
    baseURL: "http://localhost:8000"
  },
//~略~

次に実際のページの編集をしていきます。
今回は簡便にするために、indexを編集していきます。

./nuxt/front/pages/index.vue
<template>
  <div class="container">
    <div>
      <logo />
      <h1 class="title">
        front
      </h1>
      <h2 class="subtitle">
        My doozie Nuxt.js project
      </h2>
      <div class="links">
        <a
          href="https://nuxtjs.org/"
          target="_blank"
          class="button--green"
        >
          Documentation
        </a>
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey"
        >
          GitHub
        </a>

<!-- ここから追記 -->
        <div v-for="d in dat.person_data" :key=d.person_name align="center">
          <h2>
            {{d.person_name}} {{d.person_age}} 
          </h2>
        </div>
<!-- ここまで追記 -->

      </div>
    </div>
  </div>
</template>

<script>
import Logo from '~/components/Logo.vue'

export default {
  components: {
    Logo
  },

//ここから追記
  data() {
    return {
      dat: []
    }
  },
  async mounted(){
    const url = "/api/get_person/" 
    const response = await this.$axios.get(url)
    this.dat = response.data
  }
//ここまで追記
}
</script>
//~略~

このままではCORSにひっかかるので、django側の設定をいじります。
setting.pyに追記します。

./django/composeexample/setting.py
# ~略~
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp', 
    'rest_framework', 
    'corsheaders', #追加
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware', # 追加
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
# ~略~

### 末尾に下記を追加

# 許可するオリジン
CORS_ORIGIN_WHITELIST = [
    'http://localhost:3000',
]
# レスポンスを公開する
CORS_ALLOW_CREDENTIALS = True

下記のようにAPI経由で先ほどテーブルに入れたデータが取得できています。
image.png

おわりに

以上でAPI経由でのdjangoとNuxt.jsの簡単な連携ができました。
上記は開発環境のものなので、本番用にまたチューニングする必要があるかと思いますが、
勉強のため一度この段階で投稿させていただきました。

49
50
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
49
50