【Python】npmを使って、Djangoにtailwindcssを導入する

## はじめに
Djangoでテンプレート層の開発をする際、ネイティブCSSでスタイルを適用しようとすると、重複したクラスを定義したり、使われていないクラスを定義してしまいがちです。

これは筆者自身もフロントエンド技術が未熟であるという問題もありますが、多くの人が同様の悩みを抱えていると思います。

そのため筆者は普段、ネイティブCSSでスタイルを決めることはほとんどせず、多くの場合、tailwindcssを使ってフロントエンドの実装を行います。

毎度新しいプロジェクトを立ち上げる際に、tailwindcssの導入方法を調査していたため、導入方法にまとまりがなく、毎回微妙な誤差が発生していました。その差をなくしたいという意味と今後の備忘録としてまとめておこうと思います。

本記事の実際の挙動が確認できるリポジトリを貼付しております。
お急ぎの方はこちらへどうぞ。

https://github.com/jugeeem/django-tailwindcss-npm-sample

## 実行環境
- Python: 3.12.2

## 免責事項
表題の説明にフォーカスを当てるために、以下の免責事項に関しては、本記事にて詳細な解説はしません。

- Djangoに関する基本的な操作
- tailwindcssに関する基本的な操作
- npmに関する基本的な操作

## 本文
Djangoにtailwindcssを導入するには、いくつかの方法がありますが、本記事では、npmを用いた導入について解説します。

### Djangoプロジェクトの立ち上げと初期設定
それでは基本となるDjangoのプロジェクトを立ち上げまでやります。
```
Bash

# 任意のディレクトリ作成
$ mkdir django-tailwindcss

# ディレクトリ移動
$ cd django-tailwindcss

# Pythonの仮想環境の構築
$ python -m venv venv

# 作成した仮想環境のアクティベート  /  ディアクティベート *親切な私は仮想環境の終了の仕方まで書いておくのだ
$ venv/Scripts/activate  /  $ deactivate

# 仮想環境が立ち上がっているのを確認出来たら以下を実行

# pip本体のアップグレード *Pythonバージョンは最新ですが念の為
$ pip install --upgrade pip

# Djangoのinstall
$ pip install django

# Djangoプロジェクトの立ち上げ *末尾のドットは同階層作るという意味
$ django-admin startproject config .

# Djangoアプリの立ち上げ
$ python manage.py startapp sample
```
コマンドラインによるDjangoプロジェクトの立ち上げは完了です。
現時点でのディレクトリ構造を確認します。
```
django-tailwindcss
├ manage.py
├ config
    ├ __init__.py
    ├ settings.py
    ├ urls.py
    ├ asgi.py
    └ wsgi.py
└ sample
    ├ __init__.py
    ├ admin.py
    ├ apps.py
    ├ migrations
        └ __init__.py
    ├ models.py
    ├ models.py
    └ views.py
```
ディレクトリ構成の確認ができましたら、本記事の表題にある通りtailwindcssを導入する準備をします。
カレントディレクトリで以下のコマンドを実行します。
```
Bash

# templatesディレクトリの新規作成
$ mkdir templates

# staticディレクトリの新規作成
$ mkdir static

# static/cssディレクトリの新規作成
$ cd static
$ mkdir css
```
改めて、ディレクトリ構成を確認します。
```
django-tailwindcss
├ manage.py
├ config/
    ├ __init__.py
    ├ settings.py
    ├ urls.py
    ├ asgi.py
    └ wsgi.py
├ sample/
    ├ __init__.py
    ├ admin.py
    ├ apps.py
    ├ migrations/
        └ __init__.py
    ├ models.py
    ├ models.py
    └ views.py
├ templates/
└ static/
    └ css/
```
上記と同じディレクトリ構成になっていることを確認し、Djangoの初期設定に取り掛かります。
```config/settings.py
INSTALLED_APPS = [
    "sample.apps.PollsConfig", # 追記
    ...,
]

...

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"], # リストの中に追記
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]

...

STATIC_URL = "static/"
STATICFILES_DIRS = [BASE_DIR / "static"] # 追記
```
簡単な実装を行うために、以下の3ファイルを定義します。
- sample/views.py
- config/urls.py
- templates/index.html
```python: sample/views.py
from django.shortcuts import render

def index(request):
    context = {
        "title": "Django-tailwindcss",
        "button_body": "click me !"
    }
    return render(request, "index.html", context)
```
```python: config/urls.py
from django.contrib import admin
from django.urls import path
from sample.views import index

urlpatterns = [
    path("", index, name="index"), # 追記
    path("admin/", admin.site.urls),
]
```
```html: templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ title }}</title>
</head>
<body>
    <h1>{{ title }}</h1>
    <button>{{ button_body }}</button>
</body>
</html>
```
それでは現時点でどのような表示がされているのかを確認しましょう。
以下の画像のように出力されていますね。
![127.0.0.1_8000_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950438/4cda05da-3046-0a5b-ee73-8d5c7560490c.png)

いよいよ、tailwindcssの導入部分に入ります。
### tailwindcssの導入
ここからはtailwindcssの公式ドキュメントを参考に進めていきます。
```
# tailwindcssをnpm経由でインストール
$ npm install -D tailwindcss

# tailwind.config.jsの作成
$ npx tailwindcss init
```
作成された`tailwind.config.js`に対して、テンプレートへのパスを構成します。
```tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./templates/*.html", // 追記
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
```
さらに`static/css/`に`input.css`というcssを作成します。
作成したinput.cssにtailwindディレクティブを追記します。
```static/css/input.css
@tailwind base;
@tailwind components;
@tailwind utilities;
```
以下のコマンドを入力し、Tailwind CLI ビルド プロセスを実行します。
```
$ npx tailwindcss -i ./static/css/input.css -o ./static/css/output.css --watch
```
`static/css/`に`output.css`というcssがビルドされていると思います。
あとはテンプレートで出力した`output.css`を読み込み、簡単にスタイルを適応していきます。
```templates/index.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ title }}</title>
    <link href="{% static 'css/output.css' %}" rel="stylesheet"> <!-- 追記 -->
</head>
<body>
    <h1 class="font-serif text-5xl font-bold text-blue-600">
        {{ title }}
    </h1>
    <button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full">
        {{ button_body }}
    </button>
</body>
</html>
```
この状態で、再度`output.css`をビルドします。
```
$ npx tailwindcss -i ./static/css/input.css -o ./static/css/output.css --watch
```
ビルドが完了しましたら、テンプレートに反映されているかを確認しましょう。
```
$ python manage.py runserver
# http://localhost:8000/にアクセス
```
![127.0.0.1_8000_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950438/64315ff1-5898-8fc3-28c5-4d09b7fdca50.png)

tailwindcssのスタイルが、適応されていることが確認できましたね。

お疲れさまでした。

## まとめ
本文の冒頭の通り、本記事ではnpmを使った導入でしたが、他にもDjango関連のサードパーティパッケージとして`django-tailwindcss`というライブラリを使った導入もできます ~~2022年5月12日を最後にリリースが途絶えておりますが...~~ 。
さらに手軽に開発をしたいならCDNを使うという手もありますね。

他の導入方法やより良い効率化の方法をご存知の方がいらっしゃいましたら、コメントを頂けると嬉しいです。

最後に、tailwindcssを導入するにあたって更に拡張性を持たせるためのアイデアを用意してます。

ご興味ある方は`おまけ`へどうぞ。

## おまけ
以下、私が開発をしていく中で気づいた点と改善した点をまとめております。
### テンプレートを修正する度に毎回`npx tailwindcss -i`をするのが面倒
毎回`npx tailwindcss -i ...`とか打ってられないので、`package.json`の"scripts"に対して以下のように修正を加えます。
```package.json
{
  "devDependencies": {
    "tailwindcss": "^3.4.1"
  },
  "scripts": {
    "build": "npx tailwindcss -i ./static/css/input.css -o ./static/css/output.css --watch"
  }
}
```
この追記をすることにより、毎回毎回`npx ...`をする手間からほんの少しだけ解放されます。
この記述が完了しましたら、以下のコマンドを入力してみましょう。
```
$ npm run build
```
それでも`npx ...`を入力するよりは随分と楽になったはず...
~~ホントはこの動作も自動化したいですけど、どなたかご存知ありませんか？~~

### テンプレートへのパスの構造化  

本文に記載した`tailwind.config.js`では、以下のようなパスのテンプレートに対してtailwindcssは適切にビルドしてくれません。  
例)

- templates/example/index.html

つまり、templatesディレクトリのサブディレクトリを認識してくれないのです。
そこで、`tailwind.config.js`を以下のように修正します。
```tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./templates/**/*.html",
    "./templates/*.html",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
```
これでテンプレート内のサブディレクトリも認識させることができます。
さらにディレクトリ階層を深くする場合には、`content`の中にも深いパスを設定してあげましょう。

現状、私がしている工夫などはこの程度です。
また、他にも改善の余地がある場合はコメントにて指摘頂けると幸いです。

ありがとうございました。

## 参考文献
- [Django: 公式日本語ドキュメント](https://docs.djangoproject.com/ja/5.0/)
- [tailwindcss: 公式Installation](https://tailwindcss.com/docs/installation)