Django + Vue 最小構成開発メモ

##基本的な構成
- Django (バックエンド)
- Vue (フロントエンド)

Django + Vueはただバラバラに作るだけではうまく連携できないので、こういう構成にする必要がある（ざっくり）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/05288f1c-da69-92c6-673a-af3392599094.png)


###注意事項
文中でDjango REST frameworkをインストールしますが、この記事では特に触れません。
Django + Vueで開発を行う際にはおそらく必要となるので最小構成としてインストールしています。

###詰まったところ
- それぞれのパッケージの**バージョン**の互換性
- フォルダ構成

###環境構築
※この記事での開発環境OSはWindows 10です

最初に直接Windowsにインストールするもの

- （**必須**）Python 3.8.3
    - pipを使う
- （**必須**）node.js 12.18.0
    - npmを使う
- （**必須**）PyCharm Community
    - Python用のIDEなので本来は必須ではないが、仮想環境も立ち上げてくれるし便利。いつもありがとう
    - このチュートリアルではターミナルを含め、積極的に使っています
- （任意）git
    - いれたほうがベター

それぞれのパッケージマネージャからインストールしたもの
※インストールするタイミングは追々手順の中で示します

- Python(pip)
    - Django 3.0.8
    - djangorestframework 3.11.0
    - django-webpack-loader 0.7.0
- node.js(npm)
    - @vue/cli @4.4.6
    - webpack-bundle-tracker @0.4.3
    
##手順はここから
###1．PyCharm上で仮想環境を作る
PyCharm上の[File→New Project]から新規プロジェクトを立ち上げる

新規プロジェクト立ち上げ画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/0da122f4-fc60-0eef-ac7a-2840b359a220.png)

ターミナルを立ち上げて、仮想環境(venv)に入ってることを確認する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/63d3d76e-0976-fc4b-7071-380413627177.png)

新しいフォルダを作ってその階層にターミナルで移動する

- 今回は`django_vue`フォルダを作った
- `cd django_vue`で移動する
- これは`venv`フォルダをgitに含ませたくないから(`.gitignore`で直接指定でもOKのはず)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/5a6b796a-2736-fb9e-207b-906cd5d4822a.png)

Gitリポジトリにしておく

- `git init`で空のGitリポジトリを作る
- この先は適宜好きなタイミングでcommitしてね

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/49feba76-bd1a-7433-7417-3578f4655a66.png)

###2. Pythonの仮想環境上に必要なものをインストールする

この先pipでインストールする際にpipのバージョンが古いと怒られた時はこんなコマンドも使えます

- `python -m pip install --upgrade pip --force-reinstall` 
    - PyCharmの仮想環境（venv）にデフォルトで入っているpipのバージョンが古いのでこのメッセージが出る

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/c98836de-af8d-4cd1-b276-76a5cd696c5a.png)

まずDjango

- `pip install django` 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/dd1f4caf-55f1-cbe4-fe8f-06c5f748ca33.png)


次にdjangorestframework

- `pip install djangorestframework`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/b0d4276d-0837-839d-209d-9cd7908634f7.png)


最後にdjango-webpack-loader

- `pip install django-webpack-loader`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/bb7d3fcd-31c2-297c-c887-f2e356a80f68.png)

この時点で`pip freeze`コマンドでインストールされたPythonパッケージを見てみる

- インストールした３つ以外にも関連するパッケージがインストールされているのが見えるはず

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/daf7bc7e-da14-6b05-593b-1ab4532909d5.png)

ちなみに今はこんなフォルダ構成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/254b38a1-39c1-2df9-46c0-b287b231ec66.png)

###3. Djangoプロジェクトを立ち上げて、ちょっと設定をする
Djangoプロジェクトを立ち上げる

- `django-admin startproject django_project`
- プロジェクト名が**django_project**のプロジェクトが立ち上がる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/9bc3de6c-f95c-5d90-0ef8-82fc5426e0d7.png)

`django_project`フォルダに移動する

- `cd django_project`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/1fa246fa-c36c-dfe1-3fcb-ec15bb1c83f0.png)

Django上でアプリを立ち上げる

- `python manage.py startapp myapp`
- アプリ名が**myapp**のアプリが作成される

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/a584840f-7a22-49d7-6240-7d53b3c61b3e.png)

今のところのフォルダ構成はこんな感じ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/0201eccb-1fa4-b332-2b36-1d6b42fb01c1.png)

さらに一階層下の`django_project`フォルダ内にある`settings.py`を開き、編集する

- INSTALLED_APPS
    - どのアプリが使われるかを指定してあげる
        - `rest_framework`
        - `myapp.apps.MyappConfig`
        - `webpack_loader`

```Python:django_vue/django_project/django_project/settings.py
...

INSTALLED_APPS = [
    'rest_framework',
    'myapp.apps.MyappConfig',
    'webpack_loader',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

...
```

- WEBPACK_LOADER
    - こんな感じのものを`settings.py`の最後に追加してあげる
    - これから後に作る`frontend`フォルダ内の`webpack-stats.json`というファイルを使ってwebpackをロードするよ、という話

```Python:django_vue/django_project/django_project/settings.py
...

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/

STATIC_URL = '/static/'

# ↑ここまではDjangoプロジェクト作成時にもともと記述されているところ
# ↓追加

# Webpack loader
WEBPACK_LOADER = {
    'DEFAULT': {
        'STATS_FILE': os.path.join(BASE_DIR, 'frontend', 'webpack-stats.json'),
    }
}

```

- `settings.py`保存してね

ここでDjangoのWebサーバーを立ち上げてみる
まずはDjangoプロジェクトを立ち上げた際の初期設定がDBに反映されていないのでマイグレートする

- `python manage.py migrate`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/583c5661-ba6d-52b9-354c-85925cb6a3f5.png)

マイグレーションが終わったら開発用Webサーバーを立ち上げる

- `python manage.py runserver`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/30c71ba2-b6c3-bddc-0aea-699b69ff36a9.png)

ブラウザで http://127.0.0.1:8000/ にアクセスすると、こんな画面が出るはず

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/f769bdc9-9538-c34d-f77a-21e5f14fbb7e.png)

Djangoプロジェクトが立ち上がりました！やったね！

次の作業に進むためにWebサーバーを止めましょう

- Webサーバーを止める時はターミナルで`Ctrl + C`を押す 

###4. npmで基本的なものをインストールする

`frontend`という名前のフォルダを`manage.py`と同階層に作る

フォルダ構成はこんな感じ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/9190f260-e49c-0a7f-c997-3fe974a612eb.png)

ターミナルで`frontend`に移動する

- `cd frontend`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/febf0c8a-9565-939e-73a6-578807aff2d2.png)

`@vue/cli`をインストールする

- `npm i -g @vue/cli`
- `i`はインストールの意味
- `-g`はグローバルでインストールするという意味で、cliが関わるものは`-g`でインストールしよう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/520cece8-2990-c12d-b8fc-2d2e5ce1cc56.png)

Vueのプロジェクトを作成する

- `vue create .`
- `.`が大事で、現在のディレクトリにプロジェクトを作成できる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/29ab8457-f4d4-a133-015c-66e0030f6c25.png)

- 現在のディレクトリにインストールするか聞かれるので、`Y`と答える

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/f7dd3246-df7a-273b-6969-8967cc0cb1a1.png)

- プリセットを選べと言われるので`default (babel, eslint)`を選ぶ
    - ここでは矢印キーで移動する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/a7561af5-f0f5-d66b-89d9-d57d48d041b4.png)


- インストールが終わるのを待つ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/d5a234c6-fd1c-a848-663f-44c8092b852f.png)

- インストールが終わったらVueの開発用Webサーバーを立ち上げてみる
    - `npm run serve`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/e3c1c9a4-b570-174d-670d-f3145c026f0b.png)

- ブラウザで表示されているURL（今回は http://localhost:8081 、つまり http://127.0.0.1:8081 と同義）にアクセスすると、こんな画面になる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/cfe274a1-0bc1-98b9-b753-d6082d4d63f8.png)

- これでVue.jsアプリが立ち上がりました！やったね！
    - 終了させる時はターミナルで`Ctrl + C`
    - `バッチ ジョブを終了しますか (Y/N)?`と聞かれた場合は`Y`と入力して`Enter`でOK

###5. npmで今回必要なものをインストールする
インストールするもの

- webpack-bundle-tracker
    - さっきDjangoの`settings.py`で設定した`webpack-stats.json`を吐いてくれるやつ
    - webpack自体は@vue/cli内部で使われている

**バージョンを指定**してwebpack-bundle-trackerをインストールする
- `npm i -D webpack-bundle-tracker@0.4.3`
    - webpack-bundle-trackerのバージョン0.4.3をインストールしろ、という意味
    - `-D`は開発用のdevDependenciesとして登録される
    - `-D`は`--save-dev`でも良い
    - バージョン指定しないと最新のバージョン1.0.0alpha.1が入ってしまい、これを使うとDjango側で`TypeError: string indices must be integers`のようなエラーが出てしまう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/9d44d796-ff6a-6818-08be-8fc1f687fb3b.png)

ここまででこんなファイル構成になるはず

- `frontend`フォルダ内にたくさんのファイルができました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/b1c15f8a-553d-e427-74ab-2c2230f5d852.png)

###6. Vueプロジェクトの設定ファイルを作る
`frontend`フォルダ直下（`package.json`と同じ階層）に`vue.config.js`というファイルを作る

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/2a811d6d-7a3d-d0dc-af05-4bac961f1008.png)

`vue.config.js`に以下のように書く

```JavaScript:django_vue/django_project/frontend/vue.config.js
const BundleTracker = require('webpack-bundle-tracker')

// Change this to match the path to your files in production (could be S3, CloudFront, etc.)
const DEPLOYMENT_PATH = '/static/dist/'

module.exports = {
    publicPath: process.env.NODE_ENV === 'production' ? DEPLOYMENT_PATH : 'http://localhost:8080/',
    outputDir: '../myapp/static/dist',

    devServer: {
        public: 'localhost:8080',
        headers: {
            'Access-Control-Allow-Origin': '*',
        },
    },

    configureWebpack: {
        plugins: [
            new BundleTracker({ path: __dirname, filename: 'webpack-stats.json' }),
        ],
    },
}
```

- `outputDir`のところが重要で、ここで一階層上の**`myapp`**フォルダ内、その中の`static`フォルダ内、さらにはその中の`dist`フォルダの中にVueアプリで作成したjsなどを吐くことを指定する
- `configureWebpack`のところは、webpackの情報を保存しておくファイルのファイル名とディレクトリを指定している（先にDjangoの`settings.py`で指定した`webpack-stats.json`というファイル）

###7. Vueプロジェクトをビルドする
ビルドするとwebpackの情報が入った`webpack-stats.json`や、Vueプロジェクトファイルなどがバンドルされたファイルが生成される

- そのまま`frontend`フォルダ内で`npm run build`コマンドを実行する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/4ef54625-8277-ebb6-f0d6-acb32873d755.png)

- ファイル構成はこんな感じになる
    - `frontend`フォルダ内に`webpack-stats.json`が生成される
    - `myapp`フォルダ内に`static`、その中の`dist`内にバンドルされたファイルが生成される

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/157d881e-047d-2d3b-5da6-d67068ff439a.png)

###8. Djangoに戻り、表示するためのHTMLファイルを作る
Djangoに表示させるためのHTMLファイルを作る

- 1. `myapp`フォルダ内に`templates`フォルダを作る
- 2. `templates`フォルダ内に再度`myapp`フォルダを作る
- 3. この`myapp`フォルダ内に`index.html`を作る
    - Djangoは`templates/<Djangoアプリ名>`という名前のフォルダからHTMLを探します

階層はこうなる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/90ce9565-f2a1-dc98-caa9-bef58b4a27e5.png)

`index.html`を以下の内容に編集する

```HTML:django_vue/django_project/myapp/templates/myapp/index.html
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Vue.js + Django</title>
    </head>
    <body>
        {% load render_bundle from webpack_loader %}
        <div id="app">
            <app></app>
        </div>
        {% render_bundle "chunk-vendors" %}
        {% render_bundle "app" %}
    </body>
</html>
```

- webpack_loaderの中のrender_bundleをロードし、"chunk-vendors"と"app"を呼び出させるHTML
- `{% load render_bundle from webpack_loader %}`のロードがうまくいかないとDjangoから`OSError`で怒られる
    - その場合は`webpack-stats.json`の正しいパスが指定できていなかったりする

###9. 指定したURLに表示する項目をDjangoで指定する
**一番奥の**`django_project`フォルダにある`urls.py`を編集する
 
- 以下のスクリーンショットでハイライトされている`urls.py`を開く

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/b5afcf63-ab6d-85d8-3617-31ca9dfe2da7.png)

もともと入っているコードもあるが、それを以下のように追記する

```Python:django_vue/django_project/django_project/urls.py
"""django_project URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.0/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
from django.views.generic import TemplateView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', TemplateView.as_view(template_name='myapp/index.html'), name='myapp_index'),
]
```

- `path('', ...`というのはURLのrootを叩いた時の表示の設定をしてますよ、という意味
    - 開発環境の場合 http://127.0.0.1:8000 がrootになる
- `template_name='myapp/index.html'`と指定してあげることで「`myapp/templates/myapp/index.html`を開く」とDjangoが理解してくれる
- **本来はこのようなURLの設定方法は行わない**
    - 本来は各アプリに対して`urls.py`を作成し、そこで表示させるURLなどを指定する
    - 今回は簡便に済ませるために指定したディレクトリにある`index.html`を直接開くようにしている

###10. Djangoの開発用Webサーバーを立ち上げる
ターミナルに戻り、現在は`frontend`ディレクトリに入っているはずなので一階層上（`manage.py`がある階層）まで出る

- `cd ../`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/37fb951c-6122-4d34-07c2-029c4f18294a.png)

Webサーバーを立ち上げる

- `python manage.py runserver`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/5f88a20b-da71-c6c0-f28d-af1da675aaa9.png)

ブラウザで http://127.0.0.1:8000/ にアクセスする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670397/da123cd8-957d-4e0d-8b32-5fa01e1f30ac.png)

Vue.jsを使ったHTMLがDjangoで表示されました！やったね！

##最後に

自分で環境構築する時にうまくいかない時も結構あったので、この記事が誰かの参考になれば幸いです！