46
63

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ReactとDjangoを連携させてWeb開発を行うための環境構築&基本的なデータ通信&PaaSデプロイ

Last updated at Posted at 2024-04-27

この記事はDjangoプロジェクト内にReactを組み込んで開発するための環境構築をすることについて記述する.実行時にはDockerコンテナを使って最終的には"Hello World!"の表示と,データベースからデータを取り出すことを目標とする.その後,herokuにデプロイすることも行う.

今回の完成したコードをgithubに公開している
https://github.com/tarakokko3233/QiitaExample_WebApp_React-Django

DjangoとReactの連携

Djangoプロジェクトの作成

プロジェクトのルートディレクトリを作成.

$ mkdir yourprojectname

作成したディレクトリに移動する.

$ cd yourprojectname

環境構築用にPython仮想環境の構築を行う.

$ python3 -m venv venv

また,node.jsがインストールされていないのならば,このタイミングでインストールすること.
https://nodejs.org

Node.jsがインストールされているかどうかの確認方法は

$ npm

で表示されればOK

仮装環境を起動する.

Mac

$ source venv/bin/activate

Windows

$ venv\Scripts\activate

のちに実行時にはDockerコンテナを使用して立ち上げるので仮の仮想環境だと思って欲しい.

仮装環境に必要なライブラリをインストールする.

$ pip install django djangorestframework

Dockerコンテナ用にどのライブラリをインストールするかを記述するrequirements.txtを作成する.

$ touch requirements.txt

requirements.txtに以下のライブラリを記述する.

requirements.txt
asgiref
Django
djangorestframework
sqlparse

開発中に他のPythonライブラリが必要になったらrequirements.txtに追記すること.

Djangoのプロジェクトを作る.

$ django-admin startproject yourprojectname

作成したDjangoプロジェクトに移動する.

$ cd yourprojectname

次にDjangoプロジェクト上でReact開発用のDjangoアプリを作成する.

$ django-admin startapp frontend

アプリの名前はfrontendにしたが実際はなんでもいい.後の設定のfrontendの部分を変更することを忘れないように

settings.pyINSTALLED_APPSに必要なアプリを追加する.

settings.py
INSTALLED_APPS = [
   'django.contrib.admin',
   'django.contrib.auth',
   'django.contrib.contenttypes',
   'django.contrib.sessions',
   'django.contrib.messages',
   'django.contrib.staticfiles',
   'rest_framework', # これと
   'frontend', # これを追加する
]

settings.pyALLOWED_HOSTS以下のコードを追加する.

settings.py
ALLOWED_HOSTS = ['localhost',
                '127.0.0.1']

この時点でデータベースにデフォルトのテーブルを適用する.

$ python manage.py migrate

Reactのコードを入れるためのディレクトリを作成

この章ではカレントディレクトリをyourprojectname(ルートディレクトリ)で実行すること

Reactで作成したコードを表示するためのテンプレートを格納するためのディレクトリを作成する.

$ mkdir frontend/templates

静的ファイル(CSS,画像,main.js)格納用のディレクトリを作成する.

$ mkdir frontend/static

わかりやすいように用途ごとにディレクトリを作成する.

$ mkdir frontend/static/frontend
$ mkdir frontend/static/css
$ mkdir frontend/static/images

Reactのコードを入れるためのディレクトリを作成する.

$ mkdir frontend/src
$ mkdir frontend/src/components

node.jsのモジュールをインストール

この章ではカレントディレクトリをfrontendで実行すること.

$ cd frontend

はじめにNodeプロジェクトを初期化する.

$ npm init -y

複数のJavaScriptライブラリを一つにまとめるためのwebpackをインストールする.

$ npm i webpack webpack-cli --save-dev

npm inpm installのこと,-save-devオプションは、モジュールをローカルインストールするためのオプション

JavaScriptの最新バージョンでは最新の構文が登場するが,その構文は古いブラウザでは動作しないことがあるため,最新の構文を古いブラウザでも動くように変換するツールであるbabelをインストールする.

$ npm i @babel/core babel-loader @babel/preset-env @babel/preset-react --save-dev
$ npm i @babel/plugin-proposal-class-properties

最後にReactをインストールする.

$ npm i react react-dom --save-dev
$ npm i react-router-dom

node.jsモジュールの設定ファイルのセットアップ

webpackのconfigファイルを作成する.

$ touch webpack.config.js

webpack.config.jsに以下の内容を記述する.

webpack.config.js
const path = require("path");
const webpack = require("webpack");

module.exports = {
   entry: "./src/index.js",
   output: {
       path: path.resolve(__dirname, "./static/frontend"),
       filename: "[name].js",
   },
   module: {
       rules: [
           {
               test: /\.js$/,
               exclude: /node_modules/,
               use: {
                   loader: "babel-loader",
               },
           },
       ],
   },
   optimization: {
       minimize: true,
   },
   plugins: [
       new webpack.DefinePlugin({
           'process.env.NODE_ENV' : JSON.stringify('production')
       })
   ]
}

次に,babelのconfigファイルを作成する.

$ touch babel.config.json

babel.config.jsonに以下の内容を記述する.

babel.config.json
{
   "presets": [
       [
           "@babel/preset-env",
           {
               "targets": {
                   "node": "14"
               }
           }
       ],
       "@babel/preset-react"
   ],
   "plugins": ["@babel/plugin-proposal-class-properties"]
}

Reactのエントリーポイントであるindex.jsを作成する.

$ touch src/index.js

最後にpackage.json"scripts"を以下の内容に変更する.

package.json
"scripts": {
   "test": "echo \"Error: no test specified\" && exit 1",
   "dev": "webpack --mode development --watch",
   "build": "webpack --mode production"
 }

package.jsonにおいて,もし
"@babel/plugin-proposal-class-properties""dependencies":にあるときは,"devDependencies":に記述すること.

package.jsonはこのようになっているはずである.

package.json
{
 "name": "frontend",
 "version": "1.0.0",
 "description": "",
 "main": "index.js",
 "scripts": {
   "test": "echo \"Error: no test specified\" && exit 1",
   "dev": "webpack --mode development --watch",
   "build": "webpack --mode production"
 },
 "keywords": [],
 "author": "",
 "license": "ISC",
 "devDependencies": {
   "@babel/core": "^7.24.4",
   "@babel/preset-env": "^7.24.4",
   "@babel/preset-react": "^7.24.1",
   "babel-loader": "^9.1.3",
   "react": "^18.3.1",
   "react-dom": "^18.3.1",
   "webpack": "^5.91.0",
   "webpack-cli": "^5.1.4",
   "@babel/plugin-proposal-class-properties": "^7.18.6"
 },
 "dependencies": {
   "react-router-dom": "^6.23.0"
 }
}

テンプレートの作成

この章ではカレントディレクトリをfrontendで実行すること.

Reactのコードを表示するためのテンプレートを作成する.

$ mkdir templates/frontend
$ touch templates/frontend/index.html

index.hemlを以下のように記述する.

index.html
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Document</title>
   {% load static %}
</head>
<body>
   <div id="main">
       <div id="app"></div>
   </div>

   <script src="{% static "frontend/main.js" %}"></script>
</body>
</html>

もしCSSファイルを作成するのならば以下のコードをheadタグの一番最後に記述する.

index.html
  <!-- cssを追加する場合 -->
  <link rel="stylesheet" href="{% static "css/index.css" %}">

簡単にReactのコードを書く.frontend/src/components/App.jsを作成する.

$ touch src/components/App.js
App.js
import React, { Component } from "react"
import { render } from "react-dom"

export default class App extends Component {
   constructor(props) {
       super(props);
   }

   render() {
       return <h1>Hello World!</h1>;
   }
}

const appDiv = document.getElementById("app");
render(<App/>, appDiv)

ビューとエンドポイントの作成

yourprojectname/frontend/views.pyに以下のビューを追加する.

views.py
def index(request, *args, **kwargs):
   return render(request, 'frontend/index.html')
   

yourprojectname/yourprojectname/urls.pyに以下のビューを作成する.

yourprojectname/urls.py
from django.contrib import admin
from django.urls import path, include

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

frontendディレクトリ内にurls.pyを作成する.

$ touch urls.py

作成したfrontend/urls.pyを以下のように編集する.

frontend/urls.py
from django.urls import path
from .views import index

urlpatterns = [
   path('', index)
]

最後にindex.jsApp.jsを読み込むようにする.

yourprojectname/frontend/src/index.js
import App from "./components/App";

これで完成である.

Dockerコンテナの作成

この章ではカレントディレクトリをyourprojectname(ルートディレクトリ)で実行すること

yourprojectnameディレクトリ(ルートディレクトリ)でDockerfileを作成する.

$ touch Dockerfile
# Node.jsの公式イメージを使用
FROM node:14 as build

# 作業ディレクトリの設定
WORKDIR /app/frontend

# package.jsonとpackage-lock.jsonをコピー
COPY frontend/package*.json ./

# Node.jsの依存パッケージをインストール
RUN npm install

# 残りのフロントエンドファイルをコピー
COPY frontend/ .

# Djangoアプリ用のイメージをベースに
FROM python:3.9

# 作業ディレクトリの設定
WORKDIR /app

# 必要なパッケージをインストール
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 残りのファイルをコピー
COPY . .

# データベースファイルを永続化するためのボリュームを作成
VOLUME /app/db_data

# ポートの公開
EXPOSE 8000

# Djangoアプリの起動コマンド
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

yourprojectnameディレクトリ(ルートディレクトリ)でdocker-compose.ymlを作成する.

$ touch docker-compose.yml
docker-compose.yml
services:
  backend:
    build: .
    ports:
      - 8000:8000
    volumes:
      - .:/app
      - db_data:/app/db_data

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile.dev
    volumes:
      - ./frontend:/app
    ports:
      - 3000:3000

volumes:
  db_data:

yourprojectname/frontendディレクトリでDockerfile.devを作成する.

$ touch frontend/Dockerfile.dev
Dockerfile.dev
# Node.jsの公式イメージを使用
FROM node:14

# 作業ディレクトリの設定
WORKDIR /app

# package.jsonとpackage-lock.jsonをコピー
COPY package*.json ./

# Node.jsの依存パッケージをインストール
RUN npm install

# ソースコードをコピー
COPY . .

# ホットリロードサーバーを起動
CMD ["npm", "run", "dev"]

これでyourprojectnameディレクトリ(ルートディレクトリ)において,以下のコマンドを実行すればDockerコンテナをビルド&立ち上げができる.

$ docker compose up

この時点でdockerコンテナを用いて実行をするので最初に作成したPython仮装環境であるvenvファイルは不要である.削除してよい.

Dockerコンテナをうまく立ち上げられなかった場合
ターミナル(コマンドプロンプト,WindowsPowerShell)を2つ立ち上げる.
1つはルートディレクトリでPython仮装環境を起動したことを確認して以下のコマンドを実行する.

$ python manage.py runserver

もう1つはfrontendディレクトリで以下のコマンドを実行する.

$ npm run dev

サーバーを立ち上げたら,http://localhost:8000 にアクセスする.アクセスしたら左上にHello World!と表示されていたらOK

つぎはデータのやり取りをDjangoとReactでするための設定をする

データベースからデータを取得する

ここからはデータベースからデータを受信してテンプレートに表示する方法について記述する.今回はUsers(id,name,email)モデルを作成し,nameとemailを表示する.

モデル,ビュー,エンドポイントの作成

この章ではカレントディレクトリをfrontendディレクトリで実行する

$ cd frontend

frontendディレクトリでモデルを作成する.

models.py
from django.db import models

class Users(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField()

コンテナを立ち上げた状態(docker compose up)で以下のコマンドを実行する.これでマイグレーションファイルを作成し,データベースに適用する.

$ docker compose exec backend python manage.py makemigrations
$ docker compose exec backend python manage.py migrate

Dockerが使えない場合は仮装環境を立ちあげて以下のコマンドを実行する.

$ python manage.py makemigrations
$ python manage.py migrate

frontendディレクトリでserializers.pyを作成する.

$ touch serializers.py
serializers.py
from rest_framework import serializers
from .models import Users

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = Users
        fields = ['id', 'name', 'email']

frontendディレクトリのviews.pyに追記する.

views.py
from django.shortcuts import render
from rest_framework import viewsets
from .serializers import UserSerializer
from .models import Users

# Create your views here.
def index(request, *args, **kwargs):
   return render(request, 'frontend/index.html')

class UserViewSet(viewsets.ModelViewSet):
    queryset = Users.objects.all()
    serializer_class = UserSerializer

frontendディレクトリのurls.pyを以下のように変更する.

frontend/urls.py
from django.urls import path
from .views import index
from rest_framework import routers
from .views import UserViewSet

router = routers.DefaultRouter()
router.register('api/users', UserViewSet)

urlpatterns = [
   path('', index, name='frontend')
]+ router.urls

React側でJavaScriptでデータ表示する

この章ではカレントディレクトリをyourprojectname(ルートディレクトリ)で実行する

frontend/src/componentsディレクトリにUsersList.jsを作成する.

$ touch frontend/src/components/UsersList.js
UsersList.js
// UsersList.js
import React, { useState, useEffect } from 'react';

const UsersList = () => {
 const [users, setUsers] = useState([]);

 useEffect(() => {
   const fetchUsers = async () => {
     const response = await fetch('api/users/');
     const data = await response.json();
     setUsers(data);
   };

   fetchUsers();
 }, []);

 return (
   <div>
     <h1>UsersList</h1>
     <ul>
       {users.map((user) => (
         <li key={user.id}>
           {user.name} - {user.email}
         </li>
       ))}
     </ul>
   </div>
 );
};

export default UsersList;

frontend/src/components/App.jsUserList.jsに遷移するようのリンクを設定する.

App.js
// App.js
import React from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter as Router, Route, Link, Routes } from 'react-router-dom';
import UsersList from './UsersList';

const App = () => {
  return (
    <Router>
      <div>
        <h1>Hello World!</h1>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/users">Users</Link>
            </li>
          </ul>
        </nav>

        <Routes>
          <Route path="/users" element={<UsersList />} />
        </Routes>
      </div>
    </Router>
  );
};

const container = document.getElementById('app');
const root = createRoot(container);
root.render(<App />);

今回はUsersList.jsへのエンドポイントは/usersとする

この時点で試しにdb.sqlite3へデータを挿入すること.

データベースにアクセスするGUIソフトウェアをインストールしておくとSQliteなどデータベースを管理しやすい.
例えばTablePlusというソフトウェアがある.
https://tableplus.com

リモートやローカルのPostgreSQLやMySQLにアクセスしてもよいがその時はsettings.pyのDATABASE設定を変更すること

たとえばDBサーバとしてHerokuPostgresを使う場合の設定は以下の記事を参照すること
https://qiita.com/tarakokko3233/items/0961933e2b9695cb561a

ただし上記の記事はデプロイの仕方まで記載してあるが,今回は上記の記事の仕方でデプロイ出来ないので注意すること

データベース接続情報を直接settings.pyに書き込むことはセキュリティ上危険なので以下の記事を参考にして環境変数を使用して欲しい
https://qiita.com/tarakokko3233/items/bb63dbef9d91f44d3917

これでyourprojectnameディレクトリ(ルートディレクトリ)において,以下のコマンドを実行すればDockerコンテナをビルド&立ち上げができる.

$ docker compose up

再掲Dockerコンテナをうまく立ち上げられなかった場合
ターミナル(コマンドプロンプト,WindowsPowerShell)を2つ立ち上げる.
1つはルートディレクトリでPython仮装環境を起動したことを確認して以下のコマンドを実行する.

$ python manage.py runserver

もう1つはfrontendディレクトリで以下のコマンドを実行する.

$ npm run dev

サーバーを立ち上げたら,http://localhost:8000 にアクセスする.

PaaSにデプロイする

今回はHerokuにデプロイする.

静的ファイルの収集とALLOWED_HOSTSの追加

settings.pyに静的ファイルを収集するための設定を追記する.

settings.py
import os

ALLOWED_HOSTS = ['localhost',
                '127.0.0.1',
                '.herokuapp.com' ] #この行を追加
MIDDLEWARE = [
    # 他のミドルウェア...
    'whitenoise.middleware.WhiteNoiseMiddleware', #これを追加
]

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'frontend', 'build', 'static'),
]

STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

WhitenoiseのApacheDeployHandler設定を追加するために,wsgi.pyを変更する.

wsgi.py
# wsgi.py
import os
from django.core.wsgi import get_wsgi_application
from whitenoise import WhiteNoise

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yourprojectname.settings')

application = get_wsgi_application()
application = WhiteNoise(application)

requirements.txtへの追記

デプロイ用のPythonライブラリをrequirements.txtに追記する.

requirements.txt
asgiref
Django
django-environ
gunicorn
psycopg2
djangorestframework
django-filter
whitenoise
dj-database-url

runtime.txtの作成

デプロイ時のwebサーバで使うpythonバージョンを指定するファイルをyourprojectnameディレクトリ(ルートディレクトリ)に追加する

runtime.txt
python-3.12.2

Procfileの作成

HerokuでDjangoアプリケーションをデプロイする際に,Herokuがアプリケーションをどのように実行すべきかを指示するファイルをyourprojectnameディレクトリ(ルートディレクトリ)に追加する

web: gunicorn yourprojectname.wsgi --log-file -
react-build: npm install --prefix frontend
react-build: npm run build --prefix frontend
release: python manage.py migrate

ここまでのファイルをgitにプッシュする.プッシュする際にリポジトリに.gitignoreを追加することをお勧めする.

.gitignore
# Python
*.pyc
*~
__pycache__

# Database
db.sqlite3

# Static files
static/

# Media files
media/

# Environment
.env
.venv
env/

# Node
node_modules/
npm-debug.log

# OS files
.DS_Store
Thumbs.db

# Editor files
.vscode/
.idea/

この状態でherokuアプリケーションにプッシュしたgitリポジトリを接続してデプロイする.
herokudashboardの使い方については以下の記事を参考にすること
https://qiita.com/tarakokko3233/items/0961933e2b9695cb561a

上記の記事ではデプロイするためのファイルは今回とは異なるので注意

お疲れ様でした.

46
63
1

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
46
63

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?