第5章 ユーザー認証の彼方へ
01. 秘密のための ログイン / ログアウト機能
「秘密のプライベートギャラリー」シリーズの最後は、ログイン機能の実装で締めよう!!
これまで結構大変だったから、わりと楽かも?
実装ステップとしては、
♦️ login.html の作成&設置
♦️ ビューに「ログイン中じゃないと表示させない」クラス( LoginRequiredMixin )の継承を追加
♦️ プロジェクト urls.py にログイン機能のルーティング追加
♦️ settings.py のログイン廻りの設定
・・・といったところかな。
-
login.html の作成
まずは、作成するフォルダなんだけど、いままでの アプリ内 templates ではなく、さらにその直下に「 registration 」フォルダを作って、その中に login.html を置いてもらう必要がある。
つまり、構造としては、templates/registration となるね。そして、そのフォルダの中に login.html を作成するよ!
<!-- sg_pieces/templates/registration/login.html -->
{% extends 'sg_pieces/base.html' %}
{% block title %}秘密のプライベートギャラリー{% endblock %}
{% block content %}
<h1 class="container text-center mb-4 mt-4">秘密のプライベートギャラリー</h1>
<div class="container mt-3" style="max-width: 400px;">
<h3 class="text-center mb-4"> 🔐 ログイン 🔐</h3>
<form method="post" class="text-center mb-4">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-outline-info w-50 mt-4">ログイン</button>
</form>
</div>
{% endblock %}
{{ form.as_p }}を使わないログインフォーム
{{ form.as_p }}を使用すると、すべてのフォームフィールドが一気に並んでしまう!
だから、個々のフィールド UI を CSS でカスタムしたい人には不向きなんだよね。
その場合は、こんな感じで、フォームをバラすことも可能!
<!-- sg_pieces/templates/registration/login.html -->
{% extends 'sg_pieces/base.html' %}
{% block title %}秘密のプライベートギャラリー{% endblock %}
{% block content %}
<h1 class="container text-center mb-4 mt-4">秘密のプライベートギャラリー</h1>
<div class="container mt-3" style="max-width: 400px;">
<h3 class="text-center mb-4"> 🔐 ログイン 🔐</h3>
<form method="post" class="text-center mb-4">
{% csrf_token %}
<div class="mb-3">
<label for="{{ form.username.id_for_label }}" class="form-label">ユーザー名</label>
{{ form.username }}
{{ form.username.errors }}
</div>
<div class="mb-3">
<label for="{{ form.password.id_for_label }}" class="form-label">パスワード</label>
{{ form.password }}
{{ form.password.errors }}
</div>
<button type="submit" class="btn btn-outline-info w-50 mt-4">ログイン</button>
</form>
</div>
{% endblock %}
自分の作り方に合う方を選んでね🌻
-
ビューに「ログイン中じゃないと表示させない」クラスの継承を追加
Django では、ログイン認証の便利機能として LoginRequiredMixin というクラスが用意されているよ。
これを継承して CBV でクラスを作成すれば、そのクラスを読み込むためには、ログイン認証が必須になる!
説明よりも、実際のコードを見た方が理解できると思う!
# sg_pieces/views.py
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic import CreateView, ListView, DetailView, UpdateView, DeleteView
from django.contrib import messages
from .models import GalleryPiece
from .forms import GalleryPieceForm
@login_required
def index(request):
return render(request, "sg_pieces/index.html")
class GalleryPieceCreateView(LoginRequiredMixin, CreateView):
model = GalleryPiece
template_name = "sg_pieces/piece_form.html"
form_class = GalleryPieceForm
class GalleryPieceUpdateView(LoginRequiredMixin, UpdateView):
model = GalleryPiece
template_name = "sg_pieces/piece_form.html"
form_class = GalleryPieceForm
class GalleryPieceListView(LoginRequiredMixin, ListView):
model = GalleryPiece
template_name = "sg_pieces/list.html"
context_object_name = "pieces"
ordering = ["id"]
paginate_by = 10
class GalleryPieceDetailView(LoginRequiredMixin, DetailView):
model = GalleryPiece
template_name = "sg_pieces/detail.html"
class GalleryPieceDeleteView(LoginRequiredMixin, DeleteView):
model = GalleryPiece
template_name = "sg_pieces/delete.html"
success_url = reverse_lazy("piece_list")
def post(self, request, *args, **kwargs):
obj = self.get_object() # 削除対象を取得
messages.success(request, f"{obj.id}. {obj.name}を削除しました。")
return super().post(request, *args, **kwargs)
class GalleryPieceView(LoginRequiredMixin, ListView):
model = GalleryPiece
template_name = "sg_pieces/gallery.html"
context_object_name = "pieces"
paginate_by = 3
全部のビューの左側に「 LoginRequiredMixin 」をくっつけるだけ!
ただし、クラスの継承は左側から順番に行われるから、必ず LoginRequiredMixin はビューの左側に書くことが大事だよ⭐︎
そして、もうひとつ注目ポイントがあるのだよ。
from django.contrib.auth.decorators import login_required
@login_required
def index(request):
return render(request, "sg_pieces/index.html")
- @login_required
これは デコレータ と呼ばれるもので、関数ビューにくっつけて使うもの。
request を引数にとる関数ビュー(FBV)の前に付けるだけで、「ログイン必須ビュー」になるよ!
ただくっつけるだけで良いなんて、うひょ〜〜〜便利!
でも、そんな便利なものなら、CBV のビューにもくっつけて、
@login_required
class GalleryPieceView(ListView):
model = GalleryPiece
template_name = "sg_pieces/gallery.html"
・・・みたいにしちゃえば?って思ったりする?
でもね、デコレータは、Django のクラスビュー(CBV)にはくっつけられない。
その理由は、クラスメソッドと関数ビューの変換とか、Django の処理手順の話も関係してくるから、いまは言及するタイミングじゃないかな。
だから、下記のように覚えてしまえばOK!
- クラスビューでログイン必須にしたいときは「 LoginRequiredMixin 」を継承する
- 関数ビューでログイン必須にしたいときは、「 @login_required 」デコレータを頭につける
これで完璧🌻
def index() を CBV で書き直したい場合はこちら
いま、views.py は index 以外、すべて CBV で書いているよね。
これも統一感を持たせて CBV で書きたい場合は、views.py と urls.py を書きかえればいい。
元の FBV のところもコメントアウトで残してあるから、お好みで好きな方を有効にして使ってみてね。
# sg_pieces/views.py 書きかえ箇所のみ
# @login_required
# def index(request):
# return render(request, "sg_pieces/index.html")
class GalleryIndexView(LoginRequiredMixin,TemplateView):
template_name = 'sg_pieces/index.html'
# sg_pieces/urls.py 書きかえ箇所のみ
# path('', views.index, name='index'),
path('', views.GalleryIndexView.as_view(), name='index'),
- プロジェクト urls.py にログイン機能のルーティング追加
# プロジェクトの urls.py。アプリ内の urls.py じゃないことに注意!
from django.contrib import admin
from django.urls import path, include
from django.contrib.auth import views as auth_views
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('sg_pieces.urls')),
path('login/', auth_views.LoginView.as_view(template_name='registration/login.html'), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
]
- settings.py にログイン回りの設定
# settings.py
LOGIN_URL = "/login/" # 未ログインでアクセスしたときに飛ばされる先
LOGIN_REDIRECT_URL = "/" # ログイン成功後に飛ぶ先
LOGOUT_REDIRECT_URL = "/login/" # ログアウト後に飛ぶ先
02. ログイン中画面の表示はどうする?
機能としては、実装が完了したね。
ここでは、現在ログイン中のとき、どのように画面上に表示するのかを設定していくよ!
まずは突然ですが、base.html を下記に書きかえていただけるかな?
<!-- sg_pieces/templates/sg_pieces/base.html -->
{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}PRIVATE GALLERY{% endblock %}</title>
<link rel="stylesheet" type="text/css" href="{% static 'sg_pieces/css/style.css' %}">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<nav class="navbar navbar-light bg-light justify-content-end px-3">
{% if user.is_authenticated %}
<span class="navbar-text me-3">ログイン中:{{ user.username }}</span>
<span class="navbar-text me-3"><a href="{% url 'index' %}">TOP</a></span>
<span class="navbar-text me-3"><a href="{% url 'admin:index' %}">管理画面</a></span>
<form method="post" action="{% url 'logout' %}">
{% csrf_token %}
<button type="submit" class="btn btn-sm btn-outline-secondary">ログアウト</button>
</form>
{% endif %}
</nav>
<main class="container py-1">
{% block content %}
<!-- ページごとの中身がここに入る -->
{% endblock %}
</main>
</body>
</html>
ここで出てきた user は、Django がテンプレートに自動で渡してくれるオブジェクト!
最初に作った sg_user(CustomUser)のユーザーがログイン中なら、ここにデータが入るよ。
-
{% if user.is_authenticated %} … {% endif %}
ログイン中なら True、ログアウト済なら False。
これで「ログインしている人にだけ見せるナビバー」を作ったよ。 -
{{ user.username }}
user オブジェクトの中から username を取り出して表示。
自分の名前が表示されると「ログインした」感が出るし、分かりやすいよね。 -
ログアウト部分だけは <form> を使って POST しているよ。
これは Django が LogoutView は安全のために POST で送信を推奨しているから。
{% csrf_token %} を使えば、CSRF(クロスサイトリクエストフォージェリ)対策ができる。だから、特別な仕様の理由がない限り、公式推奨に従うのがベターかな。
下記の2つに関しては、次のパートでやるから、とりあえずコードの記入だけしておいてね!
♦️ {% load static %}
♦️ <link rel="stylesheet" type="text/css" href="{% static 'sg_pieces/css/style.css' %}">
03. まさかの最後に static
ログイン機能の実装までを目標に始めたこのシリーズ。
実は、まだ説明していなかった大事なテーマがあるんだよね。
その名も、static ファイル!!!
🟦 「え?static って、すでに urls.py で扱ってなかったっけ?」
そう思った人。
記憶力良すぎて心配になる笑
たしかに、urls.py 内に書いた!
アップロードした画像(media/)を配信をするための設定だったよね。
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
開発中だけ Django が直で MEDIAファイル(ユーザーがアップロードする画像とか)を配信してくれる便利機能を備えた関数のことでした。
でも、今回取り上げるのは staticファイルのことで、ちょっと違う。
プロジェクトに最初から用意しておく CSS / JavaScript / アイコン / 画像 のこと。
ユーザーがアップロードするんじゃなくて、開発者が Webアプリケーションで使うために「あらかじめ置いておく固定ファイル」のことだから、静的ファイル配信関数の static() とは違うの!
そう。ここは、わりと混乱するポイントだから、おさえて欲しい!
🟦 「static() 関数」と「static フォルダ」は別モノだということを!!
- static() 関数(urls.pyで使うやつ)は?
Django が「開発モード(DEBUG=True)のときだけ、指定したフォルダをローカルサーバー経由で配信するよ〜」という便利関数。
通常は、下記コードのように、urlpatterns += static() のように使うよ。
from django.conf import settings
from django.conf.urls.static import static
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
- 「 staticファイル 」は?
CSS / JSファイル / アイコン画像などの、開発者があらかじめ置いておくファイルの総称。
CSS や JS を static フォルダに置いたときに、それらをまとめて staticファイル と呼ぶの。
こちらは、settings.py にルート/URL 設定をして、{% load static %} というテンプレートタグとセットで使用する。
static() 関数 = Django の便利機能(配信用の関数)
static フォルダ = CSS / JSなどのファイルの置き場所
名前は同じでも、役割は全然違うことがお分かりいただけたかな?
Django の公式が「static file = 静的ファイル」って呼んでるから、そこに関数もフォルダも乗っかっちゃってるだけ。雑。混乱する笑
ということで、static フォルダを設置していくよ!
場所は、sg_pieces アプリ内。
下記の構造で、style.css ファイルまで作成してくれ!
sg_pieces/static/sg_pieces/css/style.css
そして、設置場所、分かったかな?
一応、現在のディレクトリ構造を確認してみるね。
secret_gallery
├── db.sqlite3
├── manage.py
├── media
│ └── images
│ ├── image_ahiru.png
│ ├── image_ankho.png
│ └── image_mendako.png
├── secret_gallery
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── sg_pieces
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── forms.py
│ ├── models.py
│ ├── static
│ │ └── sg_pieces
│ │ └── css
│ │ └── style.css
│ ├── templates
│ │ ├── registration
│ │ │ └── login.html
│ │ └── sg_pieces
│ │ ├── base.html
│ │ ├── delete.html
│ │ ├── detail.html
│ │ ├── gallery.html
│ │ ├── index.html
│ │ ├── list.html
│ │ └── piece_form.html
│ ├── tests.py
│ ├── urls.py
│ └── views.py
└── sg_user
├── __init__.py
├── admin.py
├── apps.py
├── models.py
├── tests.py
└── views.py
上記のツリーから sg_pieces/static/sg_pieces/css/style.css は見つけられた?
その位置に、ファイルを作ってほしい。
そして、style.css ファイルの中身はこちら。
.navbar a {
font-size: 14px;
text-decoration: none;
border: none;
background: none;
outline: none;
}
.navbar a:hover {
text-decoration: underline dotted;
cursor: pointer;
}
最低限の css だけど、ログイン中の表示周りを少し整えているよ!
ここに css を自由に書いて、画面をカスタマイズしてみるのも良いね😊
次は、settings.py の設定に取りかかろう・・・と言いたいところだが、開発環境において settings.py に static 設定は、特に必要ない!
なぜなら、デフォルトで書かれた下記の記述で十分だから!
STATIC_URL = "static/"
base.html に、まだ触れていなかったコードがあったね。
♦️ {% load static %}
♦️ <link rel="stylesheet" type="text/css" href="{% static 'sg_pieces/css/style.css' %}">
- {% load static %}
これは、static ファイルを読み込むためのテンプレートタグ。 - <link rel="stylesheet" type="text/css" href="{% static 'sg_pieces/css/style.css' %}">
{% static 'sg_pieces/css/style.css' %} は staticファイルのURLを自動で組み立ててくれるタグ。
STATIC_URL の設定をもとに、正しいパスに変換してくれるよ。
{% static %} を書くと、STATIC_URL の設定をもとに、Django が自動的にルートディレクトリやアプリ内の static ディレクトリを探してくれる。
例えば、STATIC_URL = "static/" の場合、
{% static 'sg_pieces/css/style.css' %} → /static/sg_pieces/css/style.css
というURLに変換される。
🔷 どうしてわざわざテンプレートタグを使うの?
開発環境では http://127.0.0.1:8000/static/... で staticファイルが配信される。だけど、本番環境だと CDN や別サーバーから配信する場合もあるんだ。
そんなときでも Django が STATIC_URL に応じて自動で正しいURLに変換してくれるから、テンプレートの修正は一切不要!これは、{% url 'ルート名' %} と同じ考え方だよ。
URLパターンが変わっても name='xxx' が同じなら、テンプレートの修正は不要だったよね?
それと同じで、{% static 'xxx' %} を使っていれば 「どこから配信されるか」(大元のURL) が変わっても、テンプレートはそのままで大丈夫なのよ。
CDN が気になる人はこちら
CDN(Content Delivery Network)は、画像やCSS、JavaScriptみたいな「よく使うファイル」を、世界中のサーバーにコピーしておいて、ユーザーリクエストがあった場所に近いサーバーから配信してくれる仕組みのこと。
東京の人がアクセスしたら東京のサーバーから、アメリカの人がアクセスしたらアメリカのサーバーから届けてくれる!
そのおかげで「どこからアクセスしても素早い配信」が実現されるの。
いまは使わないけど、いつかグローバル対応のアプリケーションを作るときには、かなりお世話になる仕組みだよ👍
では、ここでブラウザをリロードして、index 画面を表示させてみようか!?
おそらく、static の効果が細かすぎて伝わらない笑
少し「 TOP 」「 管理画面 」の文字を小さくしただけだから!!
あと、マウスホバーのときに下線が出る!細かいっっ
あとは、お好みに合わせて style.css に追加装飾してみてください🌻
でもさすがに、これだと色味が少なすぎるので、最後にホームページのヘッダーでも設置してみようかな。
適当な画像を保存して使ってみてくれて良いけど、準備するのが面倒な人は、ぷに蔵渾身のサイトヘッダーを保存して使ってね。
こちらを使う。
この画像の保存場所は、こちら。
🔷 sg_pieces/static/sg_pieces/title_logo.png
静的ファイルなので、static の中にあることが大事!
それでは、これを index.html に設置しよう。
{% extends 'sg_pieces/base.html' %}
{% load static %}
{% block title %}秘密のプライベートギャラリー TOP{% endblock %}
{% block content %}
<div class="text-center">
<img src="{% static 'sg_pieces/title_logo.png' %}" alt="秘密のプライベートギャラリー">
</div>
<div class="list-group d-grid gap-3 mx-auto text-center mt-3" style="max-width: 300px;">
<a href="{% url 'piece_create' %}" class="list-group-item list-group-item-action">作品を登録する</a>
<a href="{% url 'piece_list' %}" class="list-group-item list-group-item-action">作品を確認する</a>
<a href="{% url 'piece_gallery' %}" class="list-group-item list-group-item-action">ギャラリー</a>
</div>
{% endblock %}
ブラウザをリロードすると・・・どう!?
・・・・はみ出してるわ笑
こんなときこそ css で調整しよう!!
style.css の一番上にロゴに関するコードを追加するので、style.css の完成形はこれ!
/* sg_pieces/static/sg_pieces/css/style.css */
.logo-img {
max-width: 300px;
height: auto;
}
.navbar a {
font-size: 14px;
text-decoration: none;
border: none;
background: none;
outline: none;
}
.navbar a:hover {
text-decoration: underline dotted;
cursor: pointer;
}
そして、index.html の画像読み込み箇所を、下記に変更。
class の追加だよ!
<!-- sg_pieces/templates/sg_pieces/index.html に一部追加のみ -->
<div class="text-center">
<img src="{% static 'sg_pieces/title_logo.png' %}" alt="秘密のプライベートギャラリー" class="logo-img img-fluid">
</div>
これで読み込むと・・・・・
ちゃんとブラウザの枠内に収まった!!!
これで本当に完成!!!!!
04. テンプレートタグの {% extends %} と {% static %}
'static' フォルダ / ファイルについては理解してもらえた?
今回は、ローカル環境における STATIC 設定と、本番環境における STATIC 設定を理解する前提知識として、{% static %}テンプレートタグについて書いていくね。
Django の静的ファイル 'static' って、わりと混乱しがち
ローカル環境であれば、settings.py にこれだけ書いてあれば、静的ファイルが配信される。
STATIC_URL = "static/"
ただし、必ずテンプレートファイルの読み込みだけは忘れちゃだめね。
これを最初に書く必要がある。
{% load static %}
あれ?
でも、さっき書いた index.html では、{% load static %} の前に {% extends 'sg_pieces/base.html' %} があったよね。
これはどういうことだ??
実はこの2つ、明確に読み込む順番が決まっている!
というか、{% extends %} で読み込むのは base.html だけ!
いっそ、そう覚えてしまってくれ!!
🟦 {% extends %} は base.html 専用のテンプレートタグなの?
YESSSSSS!!!
正確には、base.html で共通テンプレを作成しないことも可能だから、base.html 専用ではないんだけど笑
でも Django での共通テンプレートは一般的に base.html で作成するから、
{% extends %} は base.html 専用のテンプレートタグて言い切っちゃう笑
そして、{% load static %} よりも先に {% extends %} を書く理由は、
🟢 'static' よりも前に base.html を継承しておかないと、 🟠
🟢 テンプレートエンジンがブロック({% block %})の意味を解釈できないから 🟠
index.html の最後の修正は、こんな最終形態コードになったよね。
...
{% block content %}
<div class="text-center">
<img src="{% static 'sg_pieces/title_logo.png' %}" alt="秘密のプライベートギャラリー" class="logo-img img-fluid">
</div>
...
{% endblock %}
{% block content %} 内で {% static %} を使っている。
ということは、先に {% block %} の位置をテンプレートタグが理解しておかないと、Django はテンプレートタグ迷子(エラー)になってしまうの。
だから、読み込み順番とても大事!!
{% extends %}
{% load %}
settings.py の STATIC 設定の話をしようと思ったが、だいぶズレてきたね。
でも、もう少しズレたまま、テンプレートタグの話をしておきたい。
{% extends %} が base.html 専用テンプレートタグだと言ったけど、もしかすると、他にも切り出して使いたい HTMLテンプレートファイルが、無いとは言えないよね。
たとえば、すっごく簡単だけど、こういうテンプレート。
例: _back_to_top.html
<div>
<a href="{% url 'index' %}" class="mt-3 btn btn-outline-primary" style="width:130px;">トップに戻る</a>
</div>
毎回「トップに戻る」を書くのは面倒。
そんなとき、パーツとして「 _back_to_top.html 」というテンプレートを作っておいて、それを読み込めば、メンテナンス性も上がって楽できる⭐︎
そんなときに使うテンプレートタグは {% include %} で、使い方も同じ。
{% include '_back_to_top.html' %}
これで、パーツ読み込みが可能になる!
便利な小技なので、良かったら覚えてね🌻
※ テンプレートファイルの先頭につけたプレフィックスの「_(アンダースコア)」は、パーツを意味するよ(慣習的にね。絶対的な決まりではない)。
05. エラー出がちな STATIC 設定
STATIC ファイルを静的配信しようとしたとき、わりと
「static の設定が効かない!」
「style.css が反映されない」
「プロジェクト直下に cssファイル置きたい」
という声を聞くのね。
これは、ルート設定を理解すれば解決できる!
これが最後のパートだから、がんばろうね!!
まず、設定の仕方からいくね。
設定する項目は、最大で3つ。
- settings.py の設定
STATIC_URL = "static/"
STATICFILES_DIRS = [
BASE_DIR / "static",
]
STATIC_ROOT = BASE_DIR / "staticfiles"
ひとつずつ見ていこうと思う。
1つ1つちゃんと役割があるから、しっかり理解しよう!
- STATIC_URL
STATIC_URL
は、{% load static %} テンプレートタグを使用するためには、必ず必要な設定。
# そして、この設定があれば Django がアプリ直下の `static` ディレクトリを探しに行ってくれる
STATIC_URL = "static/"
※ {% load static %} が 、どこの static ディレクトリを探すのか を理解することが重要。
これを理解していないと、下記のようにルート設定する意味が理解できないの。
では、この書き方。
{% load static%}
<link rel="stylesheet" type="text/css" href="{% static 'sg_pieces/css/style.css' %}">
secret_gallery/sg_pieces/static/sg_pieces/css/style.css ← これが現在の css ルート。
ここの secret_gallery/ は「プロジェクト」だね。
そして sg_pieces/ は「 secret_gallery プロジェクト内のアプリ」だよね。だから settings.py に下記の設定があれば、
Django は sg_pieces アプリ内の static ディレクトリまで探しに行く。
- STATIC_URL = "static/" ← この設定が大事!
{% load static %} が探し出す場所は
︎▶︎▶▶︎ **secret_gallery/sg_pieces/static/ ← ここまでが検索対象そのため、「 href="{% static 'sg_pieces/css/style.css' %} 」というルートの指定をする。
しつこく言っているけど、ここを理解していれば、これからの設定を迷わない最重要ポイント!
だから、何度でも言いたい!
- STATICFILES_DIRS
次に、STATICFILES_DIRS だけど、これは追加の探索ディレクトリ。
つまり、アプリ直下以外に static フォルダを置きたい場合に設定する箇所。
逆をいえば、すべての static フォルダがアプリ内に設置してあるなら、この設定は必要ない。
# プトジェクト直下に staticフォルダ作ってファイルを置きたいのなら、この設定が必須
STATICFILES_DIRS = [
BASE_DIR / "static",
]
settings.py の 「STATICFILES_DIRS 設定の下」くらいに、こんなコードを入れ込んで runserver してみよう。
print("♦️ STATICFILES_DIRS:",STATICFILES_DIRS)
サーバー起動のターミナル画面に、ルート情報が出てくるはず。
♦️ STATICFILES_DIRS: [PosixPath('/…(略)…/secret_gallery/static')]
こんな風にルートが出力される。(…(略)… の部分は環境によって違うので、プロジェクトルートから記載)
プロジェクト直下の static フォルダを参照していることが分かったね。
・・・ということは、
この設定をすると、Django の自動探索場所が拡張されるから、プロジェクト直下に static ファイルを置くことも、むしろそれ以外の場所に置くことも可能!!
(ただ、分かりやすい場所に、分かりやすい名前で置きな・・・とは思う笑)
あ!それでも、STATIC_URL = "static/" 設定だけは必須だよ!?
これは、使わなくても書いておかないとエラーになるからね!!
💠 プロジェクト全体で使いたい css / js / アイコン / 画像 があれば、アプリ配下じゃない場所に置いた方が、プロジェクト構造的にマッチする場合もありそうね!
本当ならこの説明、 templates でも同じ構造という話ができるから出したかったんだけど、プロジェクトルート下で共通テンプレ化する構造が思い浮かばなかった🙏懺悔
- STATIC_ROOT
これは、本番用サーバーのための設定。
いままでは、Django のプロジェクトルート内の話だったよ。
そしてローカル環境であれば、STATIC_URL / STATICFILES_DIRS を設定するだけで、Django が静的ファイル配信をしてくれるんだった。
だけど、本番環境だと、静的ファイルを Django が配信することができない。
なぜなら、Django はルートディレクトリしか分からないから。
本番では https://グローバルIP/static/css/style.css みたいな配信 URL になるんだよね。
そうなると、Django では直接グローバルIP を取得してくることができないから、本番サーバーに静的ファイルを乗せられないの。
そんなときのために、STATIC_ROOT
が存在する。
STATIC_ROOT = BASE_DIR / "staticfiles"
この設定をして、本番サーバーで、staticファイルを集めるコマンドを唱える。
python3 manage.py collectstatic
そうすると、STATIC_URL / STATICFILES_DIRS に設定されている staticファイル全てが、
ルートディレクトリ直下の staticfiles というディレクトリの中に集まってくる!
そして、本番サーバーにおける静的ファイルの配信は、Nginx などの Web サーバーを介して行われる仕組みです。
(本番では、Nginx と STATIC_ROOT をつなぐ設定は、別途必要になるからね)
📕 現代社会へ降り立った Django 戦士へ
いままでお疲れさまでした!!
そして、一緒にがんばってくれてありがとう!!
ここまでで、「誰にも知られず完成させる⭐️秘密のプライベートギャラリー」が完成しました!!!
ぷに蔵はいま、すべての章を書き終わって、感無量です・・・。
本当に疲れた・・・・・
矢吹ジョーのように真っ白になりそうです。右手がずっと痺れてます。
初めて Django を触った人なら、分からないところも多かったよね。
ぷに蔵も初めて Django に触ったとき、本当に右も左も分からなくて・・・むしろ、「なにこのコード???昨日までプログラミングやってなかったっけ?この知識、本当に必要なの???」とか思ってた。
必要どころの話じゃないくらい、いまはどっぷり頭まで浸かって、Django を開発しているエンジニア様たちを、日々崇めながら使わせていただいております。
ここから、VPS レンタルサーバーを借りたデプロイをしたり、AWS / GCP などを使用したりして、開発は続いていきます。
デプロイ編や、その先にも色々機能追加を・・・と考えていましたけど、止めました。
これ以上は、無粋かな、って。
きっと、ここまで出来たのなら、自分で調べて、自分で実装する根気強さがあるはずだから、ぷに蔵が一から「ここをこうして、できたー!」ってやる必要はないと思いました。
だから一度、ここで「秘密のプライベートギャラリー」プロジェクトは完成です!!
でも、このプロジェクト自体をぷに蔵は気に入ったので、今後、これを更にゴリゴリに発展させて、個人開発で何かに使えるかな〜🧐とか思っています。
今後のエンジニア人生の中で、いつか「秘密のプライベートギャラリーシリーズで Django 始めたよ」と言ってくれる人に出会えたら良いなという願いを込めて、ここに終了を宣言させていただきます。
ありがとうございました!
Discussion