---
# System prepended metadata

title: '[2]Django Rest API + React.js + Redux + webpack 前後端整合'
tags: [redux, Django, webpack, python, React.js, Django Rest framework]

---

# [2]Django Rest API + React.js + Redux + webpack 前後端整合

###### tags: `python` `Django` `Django Rest framework`  `React.js` `redux` `webpack`
> [time= 2019 11 27 ] 

> 原文 & 參考：
> https://www.youtube.com/watch?v=GieYIzvdt2U&list=PLillGF-RfqbbRA-CIUxlxkUpbq0IFkX60&index=2

<br>

## Implementing React
*終端機目前位置 `~/django_rest_api_react/leadmanager/`*

### setting up React and webpack
新增 Django 應用程式 frontend
```=
$ python manage.py startapp frontend
```

資料結構

```
django_rest_api_react
├──leadmanager
│  ├──frontend
│  │  ├──migrations
│  │  ├──__init__.py
│  │  ├──admin.py
│  │  ├──apps.py
│  │  ├──models.py
│  │  ├──tests.py
│  │  └──views.py
│  │  
│  ├──leadmanager
│  ├──leads
│  ├──db.sqlite3
│  └──manage.py
│
├──Pipfile
└──Pipfile.lock
```

<br><br><br>

建立目錄結構來保存 React components:
```=
$ mkdir -p ./frontend/src/components
$ mkdir -p ./frontend/{static,templates}/frontend
```

資料結構

```
django_rest_api_react
├──leadmanager
│  ├──frontend
│  │  ├──migrations
│  │  ├──src
│  │  │  └──components
│  │  │
│  │  ├──static
│  │  │  └──frontend
│  │  │
│  │  ├──templates
│  │  │  └──frontend
│  │  │
│  │  ├──__init__.py
│  │  ├──admin.py
│  │  ├──apps.py
│  │  ├──models.py
│  │  ├──tests.py
│  │  └──views.py
│  │  
│  ├──leadmanager
│  ├──leads
│  ├──db.sqlite3
│  └──manage.py
│
├──Pipfile
└──Pipfile.lock
```

<br><br><br>

現在要從目前位置 `~/django_rest_api_react/leadmanager/` 移置到上一層資料夾
```=
$ cd ..
```
*終端機目前位置 `~/django_rest_api_react/`*

<br><br><br>

npm版本：
```=
$ npm -- version
```
>*6.9.0*

<br><br><br>

初始化環境：
```=
$ npm init -y
```

<br><br><br>

安裝 webpack 和 webpack cli：
```=
$ npm i -D webpack webpack-cli
```
>*webpack@4.41.2*
>*webpack-cli@3.3.10*

<br><br><br>

安裝 babel 來編譯我們的代碼：
```=
$ npm i -D @babel/core babel-loader @babel/preset-env @babel/preset-react babel-plugin-transform-class-properties
```
>*babel-loader@8.0.6*
>*@babel/preset-react@7.7.4*
>*babel-plugin-transform-class-properties@6.24.1*
>*@babel/core@7.7.4*
>*@babel/preset-env@7.7.4*

<br><br><br>

安裝 React 和 prop-types:
```=
$ npm i react react-dom prop-types
```
>*prop-types@15.7.2*
>*react@16.12.0*
>*react-dom@16.12.0*

<br><br><br>

配置Babel，新增一個檔案命名為 `./.babelrc` 在裡面寫入：
```bable=
{
    "presets": [
        "@babel/preset-env", "@babel/preset-react"
    ],
    "plugins": [
        "transform-class-properties"
    ]
}
```

資料結構
```
django_rest_api_react
├──leadmanager
├──node_modules
├──.babelrc
├──package-lock.json
├──package.json
├──Pipfile
└──Pipfile.lock
```

<br><br><br>

配置babel-loader，新增一個檔案命名為 `./webpack.config.js` 在裡面寫入：
```javascript=
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      }
    ]
  }
};
```

資料結構
```
django_rest_api_react
├──leadmanager
├──node_modules
├──.babelrc
├──package-lock.json
├──package.json
├──Pipfile
├──Pipfile.lock
└──webpack.config.js
```

<br><br><br>

現在開啟 `package.json` 配置 scripts:

將
```json=
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  }
```

改成 
```json=
"scripts": {
    "dev": "webpack --mode development ./leadmanager/frontend/src/index.js --output ./leadmanager/frontend/static/frontend/main.js",
    "build": "webpack --mode production ./leadmanager/frontend/src/index.js --output ./leadmanager/frontend/static/frontend/main.js"
  }
```

<br><br><br>

### the React frontend
新增一個檔案命名為 `./leadmanager/frontend/src/index.js` 在裡面寫入：
```javascript=
import App from './components/App';
```

<br><br><br>

新增一個檔案命名為 `./leadmanager/frontend/src/components/App.js` 在裡面寫入：
```javascript=
import React, { Component } from 'react';
import ReactDom from 'react-dom';

export class App extends Component {
    render() {
        return <h1>React App</h1>        
    }
}

ReactDom.render(<App />, document.getElementById('app'));
```

<br><br><br>

使用 Bootstrap
新增一個檔案命名為 `./leadmanager/frontend/templates/frontend/index.html` 在裡面寫入：
```htmlmixed=
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://bootswatch.com/4/cosmo/bootstrap.min.css">
    <title>Lead Manager</title>
</head>

<body>
    <div id="app"></div>
    {% load static %}
    <script src="{% static "frontend/main.js" %}"></script>
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
        integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n"
        crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
        integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
        crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.0/js/bootstrap.min.js"
        integrity="sha384-3qaqj0lc6sV/qpzrc1N5DC6i1VRn/HyX4qdPaiEFbn54VjQBEU341pvjz7Dv3n6P"
        crossorigin="anonymous"></script>
</body>

</html>
```
`./frontend/static/frontend/main.js` 由 webpack 建置時產生

資料結構
```
django_rest_api_react
├──leadmanager
│  ├──frontend
│  │  ├──migrations
│  │  ├──src
│  │  │  └──components
│  │  │  │  └──App.js
│  │  │  │
│  │  │  └──index.js
│  │  │
│  │  ├──static
│  │  ├──templates
│  │  │  └──frontend
│  │  │     └──index.html
│  │  │
│  │  ├──__init__.py
│  │  ├──admin.py
│  │  ├──apps.py
│  │  ├──models.py
│  │  ├──tests.py
│  │  └──views.py
│  │  
│  ├──leadmanager
│  ├──leads
│  ├──db.sqlite3
│  └──manage.py
│
├──node_modules
├──.babelrc
├──package-lock.json
├──package.json
├──Pipfile
├──Pipfile.lock
└──webpack.config.js
```

<br><br><br>

開起 `./leadmanager/leadmanager/settings.py` 加入應用程式 `frontend` 在 `INSTALLED_APPS`:
```python=
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'leads', 
    'frontend', # enable the frontend app
]
```

<br><br><br>


creating a views，讓 django 可以導向 `index.html`

開起 `./leadmanager/frontend/views.py` 加入:
```python=
from django.shortcuts import render

# new add
def index(request):
    return render(request, "frontend/index.html")
```

<br><br><br>

新增一個檔案命名為` ./leadmanager/frontend/urls.py` 在裡面寫入：
```python=
from django.urls import path
from . import views

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

資料結構
```
django_rest_api_react
├──leadmanager
│  ├──frontend
│  │  ├──migrations
│  │  ├──src
│  │  ├──static
│  │  ├──templates
│  │  ├──__init__.py
│  │  ├──admin.py
│  │  ├──apps.py
│  │  ├──models.py
│  │  ├──tests.py
│  │  ├──urls.py
│  │  └──views.py
│  │  
│  ├──leadmanager
│  ├──leads
│  ├──db.sqlite3
│  └──manage.py
│
├──node_modules
├──.babelrc
├──package-lock.json
├──package.json
├──Pipfile
├──Pipfile.lock
└──webpack.config.js
```

<br><br><br>

開啟 `./leadmanager/leadmanager/urls.py` 加入：
```python=
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('', include('frontend.urls')), # new add
    path('', include('leads.urls')),
]
```

<br><br><br>

Run webpack:
*終端機目前位置 `~/django_rest_api_react/`*
```=
$ npm run dev
```

執行完會在 `./leadmanager/frontend/static/frontend/` 新增一個 main.js

現在執行 django 查看 React 是否建置成功
```=
$ cd leadmanager
$ python manage.py runserver
```

前往 http://127.0.0.1:8000/

![](https://i.imgur.com/WIxduMv.png)

<br><br><br>

creating a Header

新增一個檔案命名為 `./leadmanager/frontend/src/components/layout/Header.js` 在裡面寫入：
```javascript=
import React, { Component } from 'react'

export class Header extends Component {
    render() {
        return (
            <nav className="navbar navbar-expand-sm navbar-light bg-light">
                <button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarTogglerDemo01" aria-controls="navbarTogglerDemo01" aria-expanded="false" aria-label="Toggle navigation">
                    <span className="navbar-toggler-icon"></span>
                </button>
                <div className="collapse navbar-collapse" id="navbarTogglerDemo01">
                    <a className="navbar-brand" href="#">Lead Manager</a>
                    <ul className="navbar-nav ml-auto mt-2 mt-lg-0">

                    </ul>
                </div>
            </nav>
        )
    }
}

export default Header
```

資料結構
```
django_rest_api_react
├──leadmanager
│  ├──frontend
│  │  ├──migrations
│  │  ├──src
│  │  │  ├──components
│  │  │  │  ├──layout
│  │  │  │  │  └──Header.js
│  │  │  │  │
│  │  │  │  └──App.js
│  │  │  │
│  │  │  └──index.js
│  │  │
...
```

<br><br><br>

開啟 `./leadmanager/frontend/src/components/App.js` 引入剛剛新建的 Header.js：
```javascript=
import React, { Component } from 'react';
import ReactDom from 'react-dom';
import Header from './layout/Header'; // new add

export class App extends Component {
    render() {
        return (
            <Header /> // modify
        )
    }
}

ReactDom.render(<App />, document.getElementById('app'));
```

開新的一個終端機(不要關掉 django server)， Run webpack:
*終端機目前位置 `~/django_rest_api_react/`*
```=
npm run dev
```

查看結果
![](https://i.imgur.com/5FzpX1R.png)

<br><br><br>

為了不要每次修改 JavaScript 或 React 都要執行 `npm run dev` ，
在 `./package.json` 中的 `scripts` 裡加入 `--watch` ：
```javascript=
  "scripts": {
    "dev": "webpack --mode development --watch ./leadmanager/frontend/src/index.js --output ./leadmanager/frontend/static/frontend/main.js",
    "build": "webpack --mode production ./leadmanager/frontend/src/index.js --output ./leadmanager/frontend/static/frontend/main.js"
  },
```
現在執行 `npm run dev` 後就會持續偵測你是否有改程式，當你存檔後會自動編譯
*(關閉按 `control + c`)*

<br><br><br>

新增一個檔案命名為 `./leadmanager/frontend/src/components/leads/Form.js`
在裡面寫入：
```javascript=
import React, { Component } from 'react'

export class Form extends Component {
    render() {
        return (
            <div>
                <h1>Add Leads Form</h1>
            </div>
        )
    }
}

export default Form
```

<br><br><br>

新增一個檔案命名為 `./leadmanager/frontend/src/components/leads/Leads.js`
在裡面寫入：
```javascript=
import React, { Component } from 'react'

export class Leads extends Component {
    render() {
        return (
            <div>
                <h1>Leads List</h1>
            </div>
        )
    }
}

export default Leads
```
<br><br><br>

新增一個檔案命名為 `./leadmanager/frontend/src/components/leads/Dashboard.js`
在裡面寫入：
```javascript=
import React, { Fragment } from 'react';
import Form from './Form';
import Leads from './Leads';

export default function Dashboard() {
    return (
        <Fragment>
            <Form />
            <Leads />
        </Fragment>
    )
};
```

資料結構
```
django_rest_api_react
├──leadmanager
│  ├──frontend
│  │  ├──migrations
│  │  ├──src
│  │  │  ├──components
│  │  │  │  ├──layout
│  │  │  │  ├──leads
│  │  │  │  │  ├──Dashborad.js
│  │  │  │  │  ├──Form.js
│  │  │  │  │  └──Leads.js
│  │  │  │  │
│  │  │  │  └──App.js
│  │  │  │
│  │  │  └──index.js
│  │  │
...
```

<br><br><br>

開啟 `./leadmanager/frontend/src/components/App.js` 引入剛剛新建的 Dashboard.js：
```javascript=
import React, { Component, Fragment } from 'react'; // modify
import ReactDom from 'react-dom';
import Header from './layout/Header';
import Dashboard from './leads/Dashboard'; // new add

export class App extends Component {
    render() {
        return (
            <Fragment> // new add
                <Header />
                
                // new add
                <div className="container">
                    <Dashboard />
                </div>
            </Fragment> // new add
        )
    }
}

ReactDom.render(<App />, document.getElementById('app'));
```

<br><br><br>

reload page
![](https://i.imgur.com/pVWNhzi.png)

<br><br><br>


[[3]Django Rest API + React.js + Redux + webpack 前後端整合](https://hackmd.io/@RoyChen/rksvPD6nB)
[[1]Django Rest API + React.js + Redux + webpack 前後端整合](https://hackmd.io/@RoyChen/Syzvus93S)
