Electron + React + Django REST Framework でlocal GUI アプリを作る

## Electron+react+django REST framework 
Local GUIアプリを作成する必要が出た際、言語の候補としては C#,python,Java　などが上がってくることと思います。ただし、GUIの見た目がほぼWindows95時代のアプリのごとくいけていない。。。
そこでFrontをreactで、BackendをDjangoRESTFramework で実装しElectronを使ってdesktop app 化することにしました。
情報が少なく、苦労したので、個人メモがわりに残しておきます。

参考にさせていただいたサイト:
① [Wijmo（ウィジモ）」とElectron、Reactを組み合わせて、Web技術でデスクトップアプリをつくろう] (https://codezine.jp/article/detail/13182)
② [Reactアプリから Django Rest API を叩いてみる](https://qiita.com/sand/items/80a67da0a44b042f0bc3)

方針：
1. 最初にElectron+React でLocal GUI アプリを作成する(上記①)
2. 次にDjango REST Framework を導入してAPIのend point を作成する（上記②）
3. electron　起動時にpython-shell を使って、django server を立ち上げる
4. フロントからhttp通信を使ってデータを取得する

## 方針１、２
基本的には①のサイトの流れに沿ってreact,electron のGUIアプリを作成していきます。
上記サイトに沿って作成すれば問題なく作成できると思いますし、コードを無断転載するのもよくないと思い詳細は載せません。
react+electron でGUIアプリが起動することが確認できたら、そこに②のサイトを参考にしながらDjango REST framework を導入します。

こちらも基本的に②のサイトに沿ってDjangoREST framework を導入していきます。
最終的なファイル構造は以下のようになっているはずです。

```
react-electron-django
|
|- django
|     |- db.sqlite3
|     |- manage.py
|     |- djangoMain
|     |_ djangoApp
|-- frontend
|     |-public
|     |  |-electron.js
|     |  |- preload.js
|     |-src
|        |-App.tsx
         |-index  
```

#3 electron　起動時にpython-shell を使って、django server を立ち上げる
この時点でDjangoAPIの作成ができており、electron+react のアプリもできている状態です。
ここから問題となったのは
**electronを立ち上げる際にどうやって同時にdjango serverを立ち上げて、electron が終了する際にdjango serverも終了するか**
でした。
djangoのlocal server さえ立ち上げてしまえば、後はFront からaxiosなどでAPIを叩くだけです。
方針としては、electron.js の中でpython-shell を使って command ```python manage.py runserver localhost:8000```を叩けば良いのですが、その方法がわからず苦労しました。
結論といたしまして、まず上記のelectron.js の中で

```javascript:electron.js
//pythonShell のプロセスを後でkill するためにglobal 変数を設けている
let subpy;
//アプリケーションが起動可能になったらcreateWindowを呼び出す
app.on("ready", () => {
  createWindow()

  // Django 側のサーバーを立てるコマンド
  subpy=PythonShell.run(`${path.join(__dirname, "../../django/manage.py")}`, null, function (err, result) {  // exe:'./resources/app/app.py'    edit: './app.py'
    if (err) throw err;
    console.log(result);
  });
});
```

で```python manage.py``` のコマンドを実行します。
（この際、後にelectron を閉じる時にdjango process をkill するためにGlobal変数にプロセスを格納しておきます。）
```python manage.py``` だけではサーバーは起動しません。　この後にrunserver を繋げたいのですが、、、、その方法がどうしてもわからず（ご存知の方は教えて下さい！）、結局 manage.py file にある関数```execute_from_command_line(sys.argv)``` のsys.argvのところに直接"runserver"を入れることで解決しました。

```python:manage.py
def main():
    """Run administrative tasks."""
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangotodo.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
        
    # execute_from_command_line(sys.argv)
    //書き換えたところ
    execute_from_command_line(['',"runserver"])


if __name__ == '__main__':
    main()

```

これでelectron を立ち上げた際に同時にdjango server も立ち上がるはずです。
最後にelectron を終了したときに同時にdjango server も閉じるように以下を書き加えます。

```javascript:public/electron.js
//終了時に子プロセスとその子孫を終了させる
app.on("quit", () => {
  //　ここを追加！！
  // pythonShellをkill
  subpy.childProcess.kill('SIGINT');
  
  pids.forEach((pid) => {
    try {
      process.kill(pid);
    } catch (e) {
      console.log(e);
    }
  });
});

```

## 4 front からHttp通信でデータを取得

３のプロセスが終われば、あとはフロントからAPI通信でデータを取得するだけです。

```javascript:App.tsx
import axios from "axios";
import React, {useState} from "react"

function App() {
  const [message, setMessage] = useState("top secret")

  const sendMessage = async ()=>{
    const returnMessage = await axios.get('http://localhost:8000/api/').then((res)=>{
      return res.data
    })
    setMessage(returnMessage)
  }
  return (
    <div>
      <button onClick={sendMessage}>
        テストメッセージ送信
      </button>
      <h1>
        {message}
      </h1>
    </div>
  );
}

export default App;

```

##最後に
react,electron,flask を使った記事はいくつか拝見させてもらったのですが、djangoREST framework を使った記事は少なくて苦労したので、どなたかの参考になれば幸いです。