DjangoとReactの構築及びAPI連携について～構築編～

本記事は「[エーピーコミュニケーションズ Advent Calendar 2020](https://qiita.com/advent-calendar/2020/ap-com)」の13日目のエントリとなります。
今年やったことの総まとめとして記載します。

#概要
Webアプリの構築としてバックエンドをDjango、フロントをReactで構築する。
本記事はそのための構築手順を記載するものである。またReactからDjangoのDBを参照・更新できるようDjango REST frameworkを使用してDBのAPI化も行う。完成イメージとしては以下。

![djare.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362091/e79ce20d-087a-77de-a171-e530789a48a4.png)

なお今回はDjangoとApacheの連携については省略している。apacheが動いている堤としてrunserverでDjangoを動かす。
参考：[DjangoとApacheの連携](https://www.sejuku.net/blog/27637)

⇒**思ったよりも長くなってしまったのでReactによるAPIでのデータ更新については別記事にする。**

#環境
1サーバにDjangoとReactを入れる。DBはDjangoのデフォルト(SQLite3)で今回は行うがMySQLなどにしてもらっても問題ない。今回の環境は以下。
・Centos7
・Python3.7
・Django3
・nodejs14

#Django
早速取り組んでいく。今回はyum updateしただけの真っ新なCentos7で作業を行っていく。
まず小物のインストール

```Linux:Server
[root@localhost ~]# yum install -y gcc zlib-devel libffi-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel libuuid-devel xz-devel wget
```

##Python3のインストール
次にPython3.7をインストールする。
今回Django3.0を使用するにあたりPythonのバージョンは3.6以降であれば問題ない。ソースからインストールを行う。

```Linux:Python3.7のインストール
[root@localhost ~]# wget https://www.python.org/ftp/python/3.7.9/Python-3.7.9.tgz
[root@localhost ~]#tar xzf Python-3.7.9.tgz
[root@localhost ~]#cd Python-3.7.9
[root@localhost Python-3.7.9]#./configure --enable-optimizations
[root@localhost Python-3.7.9]#make altinstall
～～～～
Successfully installed pip-20.1.1 setuptools-47.1.0
[root@localhost Python-3.7.9]# python3.7 -V
Python 3.7.9

##ここでいちいちpython3.7と打つのが面倒なのでpython3で起動するようシンボリックリンクを設定する。
[root@localhost Python-3.7.9]# which python3.7
/usr/local/bin/python3.7
[root@localhost Python-3.7.9]# ln -s /usr/local/bin/python3.7 /usr/bin/python3
[root@localhost Python-3.7.9]# python3 --version
Python 3.7.9

##次にpipをインストールする。yumなどで入れると自動的に入るが、ソースからインストールすると入らない。
[root@localhost Python-3.7.9]# curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
[root@localhost Python-3.7.9]# python3 get-pip.py
[root@localhost Python-3.7.9]# pip3 -V
pip 20.3.1 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)
```

##Djangoとrest_frameworkのインストール
```Linux:Djangoのインストール
[root@localhost Python-3.7.9]# pip3 install Django
[root@localhost Python-3.7.9]# pip3 install djangorestframework
```

##SQLite3の最新化
次にSQLite3を最新化する。理由としてはデフォルトのままだとバージョンが低くて起動時にエラーがでるからだ。
現在の最新バージョンを「[https://www.sqlite.org/download.html](https://www.sqlite.org/download.html)」で確認する。

```Linux:SQLite3の最新化
[root@localhost ~]# wget https://www.sqlite.org/2020/sqlite-autoconf-3340000.tar.gz
[root@localhost ~]# tar zxvf sqlite-autoconf-3340000.tar.gz
[root@localhost ~]# cd sqlite-autoconf-3340000
[root@localhost sqlite-autoconf-3340000]# ./configure --prefix=/usr/local
[root@localhost sqlite-autoconf-3340000]# make
[root@localhost sqlite-autoconf-3340000]# make install
[root@localhost sqlite-autoconf-3340000]# cd ..
[root@localhost ~]# find / -name sqlite3 
/root/Python-3.7.9/Doc/includes/sqlite3
/root/Python-3.7.9/Lib/sqlite3
/root/sqlite-autoconf-3340000/sqlite3
/usr/bin/sqlite3
/usr/lib64/python2.7/sqlite3
/usr/local/bin/sqlite3
/usr/local/lib/python3.7/site-packages/django/db/backends/sqlite3
/usr/local/lib/python3.7/sqlite3

##バージョンを確認する。
[root@localhost ~]# /usr/local/bin/sqlite3 --version 
3.34.0 2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b
[root@localhost ~]# /usr/bin/sqlite3 --version
3.7.17 2013-05-20 00:56:22 118a3b35693b134d56ebd780123b7fd6f1497668
[root@localhost ~]# sqlite3 --version
3.34.0 2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b
[root@localhost ~]# mv /usr/bin/sqlite3 /usr/bin/sqlite3_old
[root@localhost ~]# ln -s /usr/local/bin/sqlite3 /usr/bin/sqlite3
[root@localhost ~]# echo 'export LD_LIBRARY_PATH="/usr/local/lib"' >> ~/.bashrc
[root@localhost ~]# source ~/.bashrc
[root@localhost ~]# python3
Python 3.7.9 (default, Dec  7 2020, 23:13:43) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sqlite3
>>> sqlite3.sqlite_version
'3.34.0'
>>> exit()
```

##Djangoプロジェクトの作成
次にプロジェクトを作成する。

```Linux:Djangoプロジェクト作成
[root@localhost ~]# mkdir /home/django
[root@localhost ~]# cd /home/django/
[root@localhost django]# django-admin startproject django_react
[root@localhost django]# ls
django_react

##起動確認を行う。
[root@localhost ~]# cd django_react
[root@localhost django_react]# ls
django_react  manage.py
[root@localhost django_react]# python3 manage.py migrate
～～～～
  Applying sessions.0001_initial... OK
[root@localhost django_react]# python3 manage.py runserver
```

http://localhost:8000/ にアクセスしてDjangoの起動画面がでることを確認する。

![q1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362091/6a6104ba-8609-4020-80f1-cb368f06f30b.png)

アプリの作成も行う。
ちなみにこのあたりからファイルの更新も行っていくので個人的にはvscodeなどを使用してRemote接続できるようにしておくと楽。いちいちファイル更新するのにサーバ上でviせずに済む。私はvscodeのRemote SSHを使用して以下のようにして作業している。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362091/70ac9871-a3db-82fe-e063-0a0a2010e2f9.png)


```Linux:Djangoアプリ作成
[root@localhost django_react]# python3 manage.py startapp react_app
[root@localhost django_react]# python3 manage.py startapp api
##apiは後ほどAPIを作成するときに使用する。
```

settings.pyを編集する。

```py:/home/django/django_react/django_react/settings.py
～～～～
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'react_app',   #追加
]
```

作成したアプリがちゃんと動作するか確認していく。
react_app配下のファイルを更新していく。

```py:/home/django/django_react/react_app/views.py
from django.shortcuts import render
from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world. You're at the react_app index.")
```

```py:/home/django/django_react/react_app/urls.py
##urls.pyはデフォルトでは作成されていないので作成すること。
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
]
```

django_reactのurlsを更新する。

```py:/home/django/django_react/django_react/views.py
from django.contrib import admin
from django.urls import include,path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('react_app/', include('react_app.urls')),
]
```

runserverを実行し、[http://localhost:8000/react_app/](http://localhost:8000/react_app/)にアクセスし画面が表示されることを確認する。

![q3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362091/5ec2042c-7359-dc9f-4064-131ebaee5b68.png)

これでDjangoの基本的な設定は完了した。

#rest_framework
##設定追加
次にAPIの準備を行う。


```py:/home/django/django_react/django_react/settings.py
～～～～
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'react_app',   #追加
    'rest_framework', # 追加 
    'api.apps.ApiConfig', # 追加 
]
～～～～
# Setting permission
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ]
}
```

##DB準備
API化するDBを準備する。
今回は動作確認だけできればいいので適当にuserテーブルを作成し、そこにテストデータをいくつか投入していく。

```py:/home/django/django_react/react_app/models.py
from django.db import models

# Create your models here.

class User_Models(models.Model):
    
    class Meta:
        db_table = 'User'

    user_id = models.CharField(
        verbose_name='userid',
        blank=True,
        null=True,
        max_length=225,
        default='',
    )

    user_name = models.CharField(
        verbose_name='username',
        blank=True,
        null=True,
        max_length=225,
        default='',
    )

    user_age = models.CharField(
        verbose_name='userage',
        blank=True,
        null=True,
        max_length=225,
        default='',
    )

    user_email = models.CharField(
        verbose_name='useremail',
        blank=True,
        null=True,
        max_length=225,
        default='',
    )
    
    def __str__(self):
        return self.user_id
```

```Linux:migration
[root@localhost django_react]# python3 manage.py makemigrations
Migrations for 'react_app':
  react_app/migrations/0001_initial.py
    - Create model User_Models
[root@localhost django_react]# python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, react_app, sessions
Running migrations:
  Applying react_app.0001_initial... OK
[root@localhost django_react]# 
```

管理画面からデータ投入を行うのでadmin.pyに記載を行う。

```py:/home/django/django_react/django_react/admin.py
from django.contrib import admin

from react_app.models import User_Models

admin.site.register(User_Models)
```

管理サイトに入るためのスーパーユーザ作成

```Linux:createsuperuser
[root@localhost django_react]# python3 manage.py createsuperuser
```

作成したらrunserverし、[http://localhost:8000/admin](http://localhost:8000/admin/react_app/user_models/)にアクセス、先ほど作ったスーパーユーザでログインする。

![q4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362091/fddee009-1bfb-813a-1e3e-f8f252a11fae.png)

![q5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362091/b93195d8-ed9c-d50d-f7bb-06e68f7afb9f.png)

このように適当にテストデータを投入しておく。

##ルーティング、シリアライズの設定
次にルーティング設定とDBのシリアライズ化を行う。
apiアプリフォルダの配下にserializers.pyを作成し、以下のように記載する。

```py:/home/django/django_react/api/serializers.py
from rest_framework import serializers
from react_app.models import User_Models


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User_Models
        fields = ('user_id', 'user_name', 'user_age','user_email')
```

views.pyに次のように記載。

```py:/home/django/django_react/api/views.py
from rest_framework import viewsets, routers
from react_app.models import User_Models
from .serializers import UserSerializer


class UserApi(viewsets.ModelViewSet):
    queryset = User_Models.objects.all()
    serializer_class = UserSerializer

    def get_queryset(self):
        queryset = User_Models.objects.all()
        L_id = self.request.query_params.get('id')

        if L_id:
            queryset = queryset.filter(user_id=L_id)
        return queryset

router = routers.DefaultRouter()
router.register(r'user', UserApi)
```

ここで少し手を加えている。具体的にはurlでのqueryで検索ができるようにしている。queryでidを受けるとidでDBに検索をかけるようにしている。
実際の動きは後ほど確認する。

ルーティング設定を行う。
プロジェクトのurlに以下のように追加。

```py:/home/django/django_react/django_react/urls.py
from django.contrib import admin
from django.urls import include,path
from api import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('react_app/', include('react_app.urls')),
    path('api/', include(views.router.urls)),
]
```


これでAPIの準備が整ったのでrunserverし、[http://localhost:8000/api/user](http://localhost:8000/api/user)にアクセスする。以下のように表示されれば成功である。

![q7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362091/d1c53db0-2cee-a091-0ea8-00c88035be4d.png)

試しにurlにqueryを付与して検索を行ってみる。
[http://localhost:8000/api/user/?id=1001](http://localhost:8000/api/user/?id=1001)にアクセスしてみる。

![q8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362091/75b0479c-2211-4ab2-d776-7f0afb70bdad.png)

ちゃんとIDで検索ができていることが確認できる。

#React
##インストール

Reactを使用するには一定以上のバージョンのnodejsが必要となる。デフォルトで入っている奴だとバージョンが低いので
[https://github.com/nodesource/distributions/blob/master/README.md](https://github.com/nodesource/distributions/blob/master/README.md)
この辺を参考にnodejsの最新版をインストールする。今回は14をインストールすることにする。

```Linux:nodejs
[root@localhost ~]# curl -sL https://rpm.nodesource.com/setup_14.x | bash -
[root@localhost ~]# yum -y install nodejs
[root@localhost ~]# node -v
v14.15.1
[root@localhost ~]# npm -v
6.14.8

##npm経由でyarnをインストールする
[root@localhost ~]# npm install -g yarn
[root@localhost ~]# yarn -v
1.22.10
```

次にcreate-react-appコマンドを使用してReactのアプリを作成する。
場所はどこでも大丈夫だが、私は/home/django配下に作成する。

```Linux:React
[root@localhost django]# pwd
/home/django
[root@localhost django]# yarn create react-app django_front
～～～～
Happy hacking!
Done in 50.89s.
[root@localhost django]#

##動作確認を行う。
[root@localhost django]# cd django_front/
[root@localhost django_front]# yarn start
```

[http://localhost:3000](http://localhost:3000)にアクセスし以下の画面が表示されれば初期設定は完了。

![q9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362091/baa78d8b-4adb-184c-80ae-effa48bc4485.png)

#ReactとDjangoの連携
ここまででDjangoとReactがそれぞれ別環境で動くようになった。
最初のイメージ図にあるように本記事ではReactで作成したものをDjangoで表示できるようにする。Reactでの画面開発中はyarn startで動作を確認しながら構築し、構築が終わったらbuildをしてDjango側に静的ファイルとして配置する。


まずDjango側のアプリにindex.htmlを作成する。アプリのフォルダが増えてきたのでパスに注意する。

```html:/home/django/django_react/react_app/templates/main/index.html
<!DOCTYPE html>

{% load static %}

<html lang="en">
<head>
    <meta charset="utf-8">
    <title>React</title>
</head>
<body>
    <div id="app"></div>
    <script type="text/javascript" src="{% static 'js/bundle.js' %}" ></script>
</body>
</html>
```

ちなみにこの段階で[http://localhost:8000/react_app/](http://localhost:8000/react_app/)にアクセスしても何も表示されない。なぜならbodyで読み込んでいるjsファイルがないからだ。jsファイルはReact側でbuildを実行すると自動的に配置されるようにする。

React側でbuildの準備をする。
デフォルトのままyarn buildコマンドを使用してビルドを行うと大量のファイルが作成される。
ビルド後のファイルを１つにまとめるためwebpackをインストールして使用する。
webpackについての詳細は以下の記事が参考になる。

参考：[webpackとBabelの基本を理解する](https://qiita.com/koedamon/items/3e64612d22f3473f36a4)

必要なモジュールをインストールする。

```Linux:React
[root@localhost django_front]# yarn add @babel/preset-react babel-loader --dev
```

webpack.config.jsを作成する。ここで大事なのはentryとoutputである。今後buildするファイルが増えてくると適宜moduleにも設定を追加する必要がある。

```js:/home/django/django_front/webpack.config.js
const path = require('path');

module.exports = {
    mode: 'development',
    entry: './src/index.js',　//buildするファイル
    output: {
        filename: 'bundle.js',　//build後のファイル名
        path: path.join(__dirname, '../django_react/react_app/static/js') //buildファイルが作成される場所
    },
    module: {
        rules: [
          {
            test: /\.js[x]?$/,  
            exclude: /node_modules/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: [
                  '@babel/preset-env',
                  '@babel/preset-react' 
                ],
                plugins: ['@babel/plugin-syntax-jsx'] 
              }
            }
          }
        ]
      },
      resolve: {
        extensions: ['.js', '.jsx', '.json']
      }
};
```

buildするためのreactの画面を準備する。

```js:/home/django/django_front/src/App.js
import React from "react";

const App = () => {
  return (
    <div>
      <p>React here!</p>
    </div>
  );
};
export default App;
```

```js:/home/django/django_front/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById("app"));
```

```html:/home/django/django_front/public/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>React App</title>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
```

ここで.envファイルを作成して下記を記載し、yarn startする。

```linux:/home/django/django_front/.env
SKIP_PREFLIGHT_CHECK=true
```


![q10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362091/b613839e-4f96-566b-0feb-d542af162801.png)

この画面が出ればOK。ではbuildしてみる。


```Linux:React
[root@localhost django_front]# yarn webpack
```

すると「/home/django/django_react/react_app/static/js」配下にjsファイルが作成される。（フォルダも自動で作られる）

[http://localhost:8000/react_app/](http://localhost:8000/react_app/)にアクセスし、先ほどのport3000と同じ画面がでてくれば成功。

####.envの意味
先ほど.envファイルを作成したがあれの意味としてはyarn startするときにパッケージ間の依存関係のチェックをスキップするようにしている。
というのもbabel-loaderのバージョンが上がったらしく.envファイルを作らずyarn startをすると依存チェックに引っ掛かり起動しなくなる。今回はやや強引に依存チェックしないようにさせた。ただ今後構築を進めていく上でどんな影響がでるかわからないので早々には解決すべきものではある。(本記事ではスキップする)


#まとめ
本記事ではDjangoとReactの環境を構築し、DjangoでReactアプリが表示できるところまで行った。
実際のところ構築に関してはDockerやAnsibleを使って自動化できるところまでやれればよかったのだが時間がなく断念した。これに関しては別途やっていこうと思う。
次ではReactからDjangoのDBをAPI経由で参照・更新できるように設定を行っていく。