[2025年最新]Djangoで作ったアプリをPWA対応にする方法

### DjangoでPWAに対応させてみよう！
趣味でお年寄り・認知症の方向けアプリを作る中で、URLなどの余計なバーを排除したいと思いPWAをやってみました！
記事が少なかったり、わからないことが多く、かなり時間を使ってしまいました💦
躓いたところをわかりやすく説明していこうと思います！（当方大学生で勉強中のため至らないところずくしです💦）

### 必要なもの・準備・基礎知識
基本情報として必要なものを先に書いておきます！(ほぼすべてコピペで実装可能です！！)

・project startとappが済んでいる前提です！
・manifest.json(アプリアイコンや名前を記載する)
・service-worker.js(オフラインでも動作するようにキャッシュなどを管理する)
・base.html(link relでbase.htmlにmanifest.jsonをインポート？して他HTMLで継承して使う)
・ngrok(HTTPSでしかPWAは動作しないので、今回はngrokを使用してHTTPS通信にする)

### まずはdjango-pwaをインストールします
```
pip install django-pwa
```
### settings.pyのINSTALLED_APPSに使用するアプリとして登録します

```
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    'storages',
    'pwa', #追加
]
```
### Staticフォルダを指定します（お好みのフォルダ）
今回は、ルートディレクトリにbackendとfrontendがあり、BASE_DIRにルートが設定されているので、ルートのフロントエンドのスタティックフォルダがStaticフォルダとして指定されています。
Staticの中にmanifestやservice_worker.jsonを起きていきたいと思います。
```
"""Staticの設定"""
STATIC_URL = "/static/"
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'frontend', 'static'),
]
```

### manifest.jsonを書く
今回の場合、frontend/static/がありさらに、cssフォルダとjsフォルダを作っています
今回、manifest.jsonは
```frontend/static/manifest.json```
に作りたいと思います。

以下をコピーして、ご自身の内容に変えていきます。
```
{
    "name": "アプリの名前を書く",
    "short_name": "短い名前",
    "start_url": "/",
    "display": "standalone",
    "background_color": "#ffffff",
    "theme_color": "#000000",
    "icons": [
        {
            "src": "小さいアイコンのパス",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "大きいアイコンのパス",
            "sizes": "512x512",
            "type": "image/png"
        }
    ]
}
```

#### start_urlはpwaアプリがどこから始まるかを記載します。（めちゃ重要）
今回の場合、/を指定しているので、ルートURLからスタートになります。
例えば、
https://名前/top
というURLがあったときに、
```
"start_url": "https://名前/top",
```
と指定すると、topから始まるurlのみがPWAの対象になります。
つまり、https://名前/top/detail
などのtop/を経由するurlしか対象になりません。
すべてのサイトにPWAを適応するためには、
start_urlをルートURLから記載する必要があります。
すべてのサイトにPWAが適応されない問題があるときは、どこからPWAが開始されているかを調べてみると解決するかもしれません。

### service_worker.jsを書く
キャッシュを管理するjsらしいです。（詳しいことは知りませんが、キャッシュについていろいろと指定できるらしいです）
```
self.addEventListener('fetch', function(event) {});
```

### テンプレートでPWAを使えるようにする
今回はbase.htmlにPWA関連をインポートして、ほかのファイルにも継承して使えるようにしようと思います

```
<!DOCTYPE html>
<html lang="ja">
{% load static %}  <!-- staticを読み込む -->
{% load pwa %} <!-- pwaを読み込む -->
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="theme-color" content="#000000">
    <!-- PWA関連コピペ -->
    <link rel="manifest" crossorigin="use-credentials" href="{% static 'manifest.json' %}">
    {% progressive_web_app_meta %}
    <title>{% block title %}My Django PWA{% endblock %}</title>
    <!-- サービスワーカーを登録 -->
    <script>
        if ('serviceWorker' in navigator) {
            window.addEventListener('load', function() {
                navigator.serviceWorker.register('{% static "js/service_worker.js" %}').then(function(registration) {
                    console.log('ServiceWorker registered with scope:', registration.scope);
                }).catch(function(error) {
                    console.log('ServiceWorker registration failed:', error);
                });
            });
        }
    </script>
</head>
<body>
    {% block content %}
    <!-- コンテンツはここに挿入されます -->
    {% endblock %}
</body>
</html>

```
#### 重要なところ
crossoriginを書かないと動かなかったし、{% progressive_web_app_meta %}を指定しないと動かなかった
```
{% load pwa %} 
<!-- PWA関連コピペ -->
<link rel="manifest" crossorigin="use-credentials" href="{% static 'manifest.json' %}">
{% progressive_web_app_meta %}
```

そして、使いたいページに継承する
base.htmlにload staticを記載していても、継承先のHTMLでもload staticは必須
```
{% extends "base.html" %}
{% load static %} 
```

### これにて完成！！

### ngrokでHTTPS通信にして動作確認
↓ngrok使い方参考
https://qiita.com/Wadachan69/items/590b5b9c1c916b59a54c

python manage.py runserverして、
ngrokで
ngrok http 8000
で公開
インストールできるか確認！
![2025-01-20_16h35_29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3905869/f60ae065-1941-19a3-3b7e-77f3f0f73b7e.png)


### 躓いたところ
・manifestで正しくアイコンを指定していないと動かない
・start_urlがルートになっていなかった(途中で詳しく解説しています)
・base.htmlでのpwaの読み込み方が分からなかった


### まとめ！
情報が少なく、むずかった！！