Djangoチュートリアル3-ログイン-

Djangoチュートリアル3-ログイン-

Djangoチュートリアル3

この記事は部内での講習会用に作成された記事です。
この記事ではDjangoでのユーザー登録・認証の方法について紹介します。

前回のチュートリアルまででブログサイトのホームページとマイページを作成しました。
このまま記事投稿機能を作ってもいいのですが、これでは誰がドの記事を投稿したのかがわかりません。

ここからはブログ用のユーザー登録機能を作成し、記事をユーザーごとに管理できるようにしていきます。
今回はユーザー登録やログイン、ログアウトの機能を実装していきます。

ここで使用するDjangoアプリは前回のチュートリアルで作成したものを使用します。

Djangoチュートリアル2

Djangoによるページの移動やHTML、CSSの基礎について学習しながら簡単な自己紹介ページをハンズオン形式で作っていきます。


DBの準備

ユーザーの登録情報を管理するためにはそれを保存するためのデータベースが必要になります。
よってまずはデータベースの準備していきます。

Djangoではデフォルトでデータベースの設定がされています。
settings.pyの中身を少し見てみましょう!

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": BASE_DIR / "db.sqlite3",
    }
}

DjangoではデフォルトではSQLiteというデータベースが使用されています。
SQLiteは軽量なデータベースで、Djangoの開発や小規模なプロジェクトに適しています。

SQLiteはファイルベースのデータベースで、特別なサーバーを立てる必要がなく、簡単に使用できます。
ここの設定を変えることでMySQLPostgreSQL*、MongoDBなども使用できます。
他のデータベースを使用する場合はライブラリのインストールやサーバーの準備が別途必要になります。

今回はSQLiteを使用するので、特に設定を変更する必要はありません。
(SQLiteは軽量ですが、割と十分な性能を持っているので個人で作るときもSQLiteを使うことが多いです。)

データベースの設定はすでにされているので、設定に基づいてデータベースを作成していきます。
データベースの作成・更新をマイグレーションと呼びます。 以下のコマンドをターミナルで実行することで作成できます。

python manage.py migrate

このコマンドによってマイグレーションが実行され、データベースが作成されます。
db.sqlite3というファイルがmanage.pyと同じ階層に作成されます。

マイグレーションした際にdb.sqlite3db.sqliteの2つがある場合があります。その場合はdb.sqliteは消してしまってOKです。

ユーザーの情報を保存するためにはユーザーモデルを作成する必要があります。
Djangoではすでにユーザーモデルが用意されているので、今回は使用します。

ユーザーモデルは主に以下の情報(属性)を保存しています。

  • username(ユーザー名)
  • password(パスワード)
  • email(メールアドレス)

管理者ユーザーを作成する

データベースの管理を用意にするために管理者ユーザーを作成します。
これは特別な権限を持ったユーザーです。

以下のコマンドを実行して管理者ユーザーを作成します。
ユーザー名やメールアドレス、パスワードを入力するように求められます。
ここでは適当なユーザー名とメールアドレスで大丈夫です。

python manage.py createsuperuser
 
ユーザー名 (leave blank to use 'user'): admin
メールアドレス: admin@example.com
Password: 
Password (again):
このパスワードは ユーザー名 と似すぎています。
このパスワードは短すぎます。最低 8 文字以上必要です。
このパスワードは一般的すぎます。
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

本番環境ではパスワードはもっと強力なものを設定する必要がありますが、今回は開発環境なので適当なパスワードで大丈夫です。

これで管理者ユーザーが作成されたの特別な管理画面にアクセスできるようになります。
python manage.py runserverでアプリを起動して、/adminというURLにアクセスしてみましょう。

以下のようなログイン画面が表示されるはずです。

画像

先ほど設定したログイン名パスワードを入力してログインしてみましょう。

ログインに成功すると以下のような管理画面が表示されます。

画像

この画面ではデータベースのテーブルを管理することができます。
今なら、グループとユーザーというテーブルが表示されているはずです。

ここの画面から管理者はユーザーの追加や削除、編集ができます。
実際にWebアプリを運営する場合は、管理者権限でここからユーザーを管理することができます。

ユーザをクリックするとユーザーの一覧が表示されます。(今はadminユーザーしかいません。)

ユーザー名をクリックすればそのユーザーの詳細画面に移動します。
ここでユーザーの情報を編集したり、削除したりできます。

また右上にある追加ボタンをクリックすると新しいユーザーを追加できます。 試しにユーザーを追加してみましょう🚀


ログイン処理の実装

ユーザー管理用のアプリを作成する

ユーザーの登録やログインはメインのブログとは別の機能です。
よってここでは機能を分割するためにユーザー認証用のアプリを作成します。

アプリの作成までの流れはappを作った時と同じです。

主な流れ

  • アプリの作成
  • アプリの登録
  • URLの設定
  • テンプレートの準備

この流れは何度も繰り返して覚えちゃいましょう!!

まずはアプリを作成します。 以下のコマンドを実行して新しいアプリを作成します。

$ python manage.py startapp accounts

これでaccountsという名前のアプリディレクトリが作成されます。
次に作成したアプリをDjangoプロジェクトに登録します。

settings.pyを開いてアプリを登録しちゃいましょう。
INSTALLED_APPSのリストに以下のように追加します。

INSTALLED_APPS = [
    ... ,
    "accounts.apps.AccountsConfig"
]

これでアプリの登録が完了しました。

次にURLの設定を行います。
accountsディレクトリの中にurls.pyを作成しましょう!

urls.pyの中身は今のところは以下のようにします。

from django.urls import path
from . import views
 
urlpatterns = [
]

次にプロジェクトディレクトリのurls.pyaccountsアプリのURLを登録します。
登録するにはinclude関数を使います。 urls.pyを開いて以下のように変更します。

from django.contrib import admin
from django.urls import path,include
 
urlpatterns = [
    path("admin/", admin.site.urls),
    path("", include("app.urls")),
    path("accounts/", include("accounts.urls")), # accountsアプリのURLを追加(URLの先頭には/accounts/がつきます) #
]

次にHTMLテンプレートを保存するためのtemplatesディレクトリを作成します。
ここでtemplatesのディレクトリ構造はaccountsディレクトリで挟むようにします。

またログイン用のlogin.htmlも追加しておきましょう

testproject
└─ accounts
    └─ templates
        └─ accounts
            └─ login.html

このようにすることで、Djangoはaccountsアプリのテンプレートを正しく認識します。

またlogin.htmlのひな形を作成します。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>BlogSite</title>
  <link
    href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
    rel="stylesheet"
  />
</head>
<body>
 
    <nav class="navbar bg-body-tertiary">
        <div class="container">
            <a class="navbar-brand" href="/">ブログサイト</a>
            <a class="nav-link" href="{% url 'mypage' %}">マイページへ</a>
        </div>
    </nav>
    
</body>
</html>

これでユーザー管理用のアプリの準備ができました。


ログインページの準備

いよいよログイン機能の実装に入ります。

Djangoではユーザー認証のための便利な機能が用意されています。

認証の挙動をviews.pyにすべて書いて実装してもいいのですが、
Djangoでは認証のためのクラスベースビューが用意されています。

これによってセキュリティなどを考慮した認証機能を簡単に実装できます。

accounts/urls.pyurlpatternsにパスを追加していきます。

from django.urls import path
from . import views
 
from django.contrib.auth.views import LoginView, LogoutView # クラスベースビューをインポート #
 
urlpatterns = [
    path("login/", LoginView.as_view( 
        template_name="accounts/login.html"
    ), name="login"), 
 
    path("logout/", LogoutView.as_view(), name="logout"), 
]

Djangoの用意したクラスベースビューを使用すればviews.pyを自分で書く必要はありません。
あとはtmeplateを指定するだけでログイン画面が表示されます。

一旦/accounts/login/でページが表示されればOKです!

表示されたらログイン画面を用意していきましょう!


ログイン画面の作成

ログイン画面を作っていきます。
ログイン画面にはユーザーの入力情報を受け取る機能が必要になります。

よって入力用のフォームを作成します。
HTMLのformタグを使用します。

login.htmlを書いていきます。
bodyタグの中に以下の記述を追加してください。

    <div class="container d-flex justify-content-center align-items-center" style="min-height: 80vh;">
       <div class="card shadow p-4 rounded" style="width: 100%; max-width: 400px;">
             <h2>ログイン</h2>
            <form method="post" >
                {% csrf_token %}
                {{ form.as_p }}
                <button class="btn btn-secondary" type="submit">ログイン</button>
            </form>
       </div>
    </div>

{% csrf_token %}はDjangoにおいてセキュリティのために必要なトークンです。
これがないとセキュリティを保つためにDjango側がエラーを返します。

{{ form.as_p }}はDjango専用の機能で、フォームの入力フィールドを自動的に生成します。

これでログイン画面が完成しました。

しかし今のままでは、ログインしてもそのあとにエラーが出てしまいます。
これはログイン後にどのページに移動(リダイレクト)するかが設定されていないためです。

settings.pyの一番最後にログイン後のリダイレクト先を設定します。

LOGIN_REDIRECT_URL = '/'

これでログイン処理は完成です!
/accounts/login/にアクセスして、先ほど作成した管理者ユーザーでログインしてみましょう。
ログインに成功するとホーム画面にリダイレクトするはずです。


ログイン画面に移動できるようにする

ログイン画面に移動できるようにしましょう!
Webアプリ全般でよくあるのは、ナビゲーションバーにログインボタンを配置する方法です。

ですが今のままでは、ログインしていなくてもマイページにアクセス出来てしまいます。
よって

  • ① ログインしていない場合はログインボタンを表示する
  • ② ログインしている場合はログアウトボタンマイページを表示する

この切り替えはif文が必要になります。ですがHTMLにはif文はありません。

ここでDJangoのテンプレートエンジンが火を吹きます🔥

Djangoにはテンプレートに{% if %}というif文のような機能があります。
これを使ってログイン状態によって表示を切り替えます。

少し面倒ですが、すべてのHTMLのナビゲーションバーを以下のように書き換えます。
もし面倒であれば、一旦はホーム画面のナビゲーションバーだけ書き換えても大丈夫です。

共通のHTMLを実現するにはテンプレートの部品化が必要ですがこれには少し準備があるので今回は説明を省略します。
    <nav class="navbar bg-body-tertiary">
        <div class="container">
            <a class="navbar-brand" href="/">ブログサイト</a>
 
            <ul class="navbar-nav  flex-row gap-3">
                {% if user.is_authenticated  %}
                <li class="nav-item"><a class="nav-link" href="{% url 'mypage' %}">マイページ</a></li>
                <li class="nav-item">
                    <form action="{% url 'logout' %}" method="post">
                        {% csrf_token %}
                        <button type="submit" class="btn btn-link nav-link">ログアウト</button>
                    </form>
                </li>
                
                {% else %}
                <li class="nav-item"><a class="nav-link" href="{% url 'login' %}">ログイン</a></li>
                {% endif %}
            </ul>
        </div>
    </nav>

これでログイン状態によって表示が切り替わるようになります。

{% if user.is_authenticated %}でログインしているかどうかを判定しています。
また、最後には{% endif %}でif文を閉じています。(ないとエラーになります。)

これでログイン・ログアウトができるようになります。
しかし今ログアウトすると管理者用の画面にリダイレクトされてしまいます。

よってここでもsettings.pyにリダイレクト先を設定します。
さきほどと同じように一番最後に以下の行を追加します。

LOGOUT_REDIRECT_URL = '/'

これでログアウトすればホーム画面にリダイレクトされるようになります。

ちなみにHTMLで使ったuserはデフォルトでテンプレートに渡される変数です。
コンテキストを使って渡す必要がありません。

このようなデフォルトで渡される変数はsettings.pyTEMPLATESで設定できます。
デフォルトでは以下のようになっています。context_processorsを見てみましょう。

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [],
        "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",
            ],
        },
    },
]

django.contrib.auth.context_processors.authによってuserがテンプレートに渡されます。


ユーザー登録機能の実装

ここまででログイン機能は実装できました。
しかし、ユーザー登録機能はまだ実装されていません。

ここではユーザー登録機能を実装していきます。

ユーザー登録用のビューを作成する

ユーザー登録においてはログインのようなクラスベースビューは用意されていません。

ですが、Djangoはユーザー登録用のフォームUserCreationFormを用意してくれます。

今回はこれを使ってユーザー登録機能を実装していきます。
まずはaccounts/views.pyにコードを書いていきます。

from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import login
 
def signup(request):
    if request.method == "POST":
        form = UserCreationForm(request.POST)
 
        if form.is_valid():
            user = form.save()
            login(request, user)
            return redirect("index")
        
        else:
            return render(request, "accounts/signup.html", {"form": form})
    
    if request.method == "GET":
        form = UserCreationForm()
        return render(request, "accounts/signup.html", {"form": form})

一気に新しいことが出てきました。
まずはこのコードについて1つずつ説明していきます。

if request.method == "POST":
    ...
if request.method == "GET":
    ...

これはHTTPリクエストのメソッドを判定しています。

HTTPリクエスト(ブラウザからのアクセス)にはいくつかの種類があります。
主に使用されるのは以下の2つです。

  • GET: データを取得するためのリクエスト。主にページの表示に使用されます。(大体はGETです。)
  • POST: データを送信するためのリクエスト。主にフォームの送信に使用されます。

普段はGETで、データを送信する時はPOSTという理解で大丈夫です。

まずはこのページを表示されるのでGETでアクセスされます
GETなのでif request.method == "GET":以下が実行されます。

まずはGETリクエストが来た時の処理を見て見ましょう。

if request.method == "GET":
    form = UserCreationForm()
    return render(request, "accounts/signup.html", {"form": form})

これはDjangoの用意した登録用フォームを用意して、それをテンプレートに渡して表示しています。

後で書いていきますがHTMLでフォームが送信されれば、このページがPOSTでアクセスされます。

フォームが送信されればこのページのif request.method == "POST":以下が実行されます。

if request.method == "POST":
    form = UserCreationForm(request.POST)

POSTでアクセスされたらまず入力された情報を取得します

if form.is_valid():
    ...
else:
    return render(request, "accounts/signup.html", {"form": form})

まずif文を使ってフォームの入力情報が正しいかを判定します。(パスワードが問題ないか、ユーザー名が重複していないかなど)
もし入力情報に問題があればエラーを持たせたフォームを再度テンプレートに渡して表示します。

if form.is_valid():
    user = form.save()
    login(request, user)
    return redirect("index")

入力情報が正しければ、フォームの内容からユーザーをデータベースに保存します。
ついでに登録したユーザーでログインも行います。  

作成・ログインができたら、ホーム画面にリダイレクトしています。

これでビューの実装は完了です。


ユーザー登録用のテンプレートを用意する

次にユーザー登録用のテンプレートを用意します。(login.htmlと同じ場所です) accounts/templates/accounts/signup.htmlを作成して以下の内容を書いてください。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>BlogSite - 新規登録</title>
  <link
    href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
    rel="stylesheet"
  />
</head>
<body class="bg-light">
 
    <nav class="navbar bg-body-tertiary">
        <div class="container">
            <a class="navbar-brand" href="/">ブログサイト</a>
 
            <ul class="navbar-nav  flex-row gap-3">
                {% if user.is_authenticated  %}
                <li class="nav-item"><a class="nav-link" href="{% url 'mypage' %}">マイページ</a></li>
                <li class="nav-item">
                    <form action="{% url 'logout' %}" method="post">
                        {% csrf_token %}
                        <button type="submit" class="btn btn-link nav-link">ログアウト</button>
                    </form>
                </li>
                
                {% else %}
                <li class="nav-item"><a class="nav-link" href="{% url 'login' %}">ログイン</a></li>
                {% endif %}
            </ul>
        </div>
    </nav>
 
  <div class="container d-flex justify-content-center align-items-center" style="min-height: 80vh;">
    <div class="card shadow p-4 rounded" style="width: 100%; max-width: 400px;">
      <h3 class="mb-4 text-center">新規登録</h3>
 
      <form method="post" novalidate>
        {% csrf_token %}
        {{ form.non_field_errors }}
 
        <div class="mb-3">
          <label for="id_username" class="form-label">ユーザー名</label>
          {{ form.username}}
          {{ form.username.errors }}
        </div>
 
        <div class="mb-3">
          <label for="id_password1" class="form-label">パスワード</label>
          {{ form.password1 }}
          {{ form.password1.errors }}
        </div>
 
        <div class="mb-3">
          <label for="id_password2" class="form-label">パスワード(確認)</label>
          {{ form.password2}}
          {{ form.password2.errors }}
        </div>
 
        <button type="submit" class="btn btn-primary w-100">登録する</button>
      </form>
    </div>
  </div>
 
</body>
</html>

たくさん書いていますがform以外は全体共通の記述です。
また{{ from.as_p }}を使ってもいいのですが、すこし表示がごちゃつくので、今回は手動でフォームの各フィールドを表示しています。

これでユーザー登録画面が完成しました。
formタグの中を見てみるとmethod = "post"という記述があります。

これは先ほどのビューで説明したように、POSTメソッドでデータを送信することを意味しています。


ユーザー登録画面へのリンクを追加する

まずはURLを追加していきます。

accounts/urls.pyurlpatternspathを追加します。

urlpatterns = [
    path("login/", LoginView.as_view(
        template_name="accounts/login.html"
    ), name="login"),
    path("logout/", LogoutView.as_view(), name="logout"),
    path("signup/", views.signup, name="signup") # ユーザー登録用のURLを追加 #
]

そしてナビゲーションバーにユーザー登録画面へのリンクを追加します。
ナビゲーションバーのHTMLを以下のように変更します。

面倒であれば今はホーム画面のナビゲーションバーだけでもOKです。 index.htmlnavタグを以下に書き換えてください。

    <nav class="navbar bg-body-tertiary">
        <div class="container">
            <a class="navbar-brand" href="/">ブログサイト</a>
 
            <ul class="navbar-nav  flex-row gap-3">
                <li class="nav-item"><a class="nav-link" href="{% url 'signup' %}">新規登録</a></li> 
                {% if user.is_authenticated  %}
                <li class="nav-item"><a class="nav-link" href="{% url 'mypage' %}">マイページ</a></li>
                <li class="nav-item">
                    <form action="{% url 'logout' %}" method="post">
                        {% csrf_token %}
                        <button type="submit" class="btn btn-link nav-link">ログアウト</button>
                    </form>
                </li>
                {% else %}
                <li class="nav-item"><a class="nav-link" href="{% url 'login' %}">ログイン</a></li>
                {% endif %}
            </ul>
        </div>
    </nav>

ログアウトするためのリンクは**aタグではなくformタグを用いてPOSTメソッドで送信するようにしてください。**aタグでやると405エラーが発生します
これでユーザー登録画面にアクセスできるようになりました。
登録画面にいってユーザーを登録してみましょう!

ちゃんと登録できているかは管理者画面にアクセスしてみてください。
登録したユーザーが表示されているはずです。


マイページをユーザーごとに表示する(動的URL・パスパラメータ)

ここまででユーザー登録とログイン機能が実装できました。 しかし、マイページはまだ誰がアクセスしても同じ内容が表示されます。

よってユーザーごとにマイページの内容だけを変更させる必要があります。 デザインは変えずに内容だけを変えるにはURLを工夫することで実現できます


動的URL

よく使われる方法はユーザーIDや名前をURLに含める方法です。
他のユーザーと被らない情報をURLに含めることで、ユーザーごとに異なるURLを作成できます

画像

参考までに何かのWebサービスのマイページのURLを見てみると、URLの末尾にユーザーIDやユーザー名が含まれていることがわかります。(例えばQiita)
Djangoの標準のユーザーモデルではユーザー名が一意の設定なので今回はそれを使います。

app/urls.pymypageのURLを以下のように変更します。

from django.urls import path
from . import views
 
urlpatterns = [
    path("", views.index, name="index"),
    path("mypage/<str:username>/", views.mypage, name="mypage")
]

これはmypage/の後に文字列の変数が入ることを意味しています。
こうすることで、異なるURLを同じビューで作成できるようになります。

あとはアクセスするときにURLの値を指定すればOKです。

ナビゲーションバーのmypage用のリンクを以下のように変更してみましょう。(一旦はindex.htmlだけでもOK)

    <nav class="navbar bg-body-tertiary">
        <div class="container">
            <a class="navbar-brand" href="/">ブログサイト</a>
 
            <ul class="navbar-nav  flex-row gap-3">
                <li class="nav-item"><a class="nav-link" href="{% url 'signup' %}">新規登録</a></li>
                {% if user.is_authenticated  %}
                <li class="nav-item"><a class="nav-link" href="{% url 'mypage' user.username %}">マイページ</a></li>
                <li class="nav-item">
                    <form action="{% url 'logout' %}" method="post">
                        {% csrf_token %}
                        <button type="submit" class="btn btn-link nav-link">ログアウト</button>
                    </form>
                </li>
                
                {% else %}
                <li class="nav-item"><a class="nav-link" href="{% url 'login' %}">ログイン</a></li>
                {% endif %}
            </ul>
        </div>
    </nav>

{% url 'URL名' %}の中に変数や値を書くことで先ほどの変数の中に値が入ります。
user.usernameは現在ログインしているユーザーのユーザー名です。

この記事を書いてて思ったのですが、このページの名前をmypageにしたのは少し誤解を招くかなと思いました、、。今回の使い方ならmypageよりもprofileなどの方が良かったですね

これでURLの準備もOKです。
しかしこのままでもリンクを押すとエラーが出てしまいます。

よってapp/views.pyのmypageのビューを修正していきます。

from django.shortcuts import render
def index(request):
    return render(request, "app/index.html")
 
def mypage(request, username):
    return render(request, "app/mypage.html",)

URLに入れた変数の値を受け取る必要があるのでこのような記述を追加しています。

これでマイページの準備OKです! ユーザーを変更してマイページにアクセスしてみましょう。
URLが変わってもページが表示されるのが確認できるはずです。

もしこれでもエラーが出る場合は、他のページのナビゲーションバーに原因があります。 他のページのナビゲーションバーではmypageにアクセスするときにuser.usernmaeを渡していないためURLが作成されずエラーになります。 エラーを消すにはとりあえず他のページでは消すか、user.usernmaeを上と同じように渡してください。

ユーザーごとに内容を変える

ユーザーごとに異なるURLを作成できました。 ですが、まだユーザーごとに内容は変えられていません。

ユーザーごとに表示を変えるにはそのページのユーザー情報が必要です!

ここで活用するのがURLの変数です。
URLの変数にはユーザー名(一意)を使っているのでこれを使って検索をしましょう!!

画像

データベースとのアクセスはビューのお仕事です。
よって処理はapp/views.pyのmypageを更新します。

from django.shortcuts import render, get_object_or_404
from django.contrib.auth.models import User 
 
def index(request):
    return render(request, "app/index.html")
 
def mypage(request, username):
    profile_user  = get_object_or_404(User, username=username) 
    context = {"profile_user": profile_user } 
 
    return render(request, "app/mypage.html", context=context)

get_object_or_404という命令でusernameを使って検索しています。

このようなDBとのアクセスはORMという機能によって実現さえています。詳しくは次回のチュートリアルで扱います。

後は検索で出たユーザー情報をHTMLに渡すだけです。

app/mypage.htmlのユーザー名のところを以下のように変えてみましょう。

...
  <div class="container my-5 d-flex flex-column align-items-center gap-4">
    <div class="container-fluid d-flex flex-column align-items-center">
      <img
        src="https://cdn.discordapp.com/embed/avatars/0.png"
        alt="プロフィールアイコン"
        class="rounded-circle shadow mb-3"
        width="96"
        height="96"
      />
      <div class="h3 fw-bold">{{profile_user.username}}</div>
      <div class="text-muted">~学部 / ~学科</div>
      <div class="text-muted">ひとこと</div>
    </div>
...

Djangoのテンプレートエンジンのおかげで{{}}の中にビューで渡した変数を入れられるので表示を動的に変えられています。 これでユーザーごとにマイページの表示が変わるようになりました。

変数名をprofile_userとしたのはDjango側でデフォルトで用意されているuser変数との衝突を回避するためです。

📚 参考資料

Djangoの認証システムを使用する

https://docs.djangoproject.com/ja/5.2/topics/auth/default/

他のStringを探す