Django私的学習備忘録 -ログイン機能を実装しユーザ毎に表示内容を切り替える-

# はじめに
Djangoの私的学習メモです。
ChatGPT4にて調べた内容と追加で調べた内容を見返せるように記事としてまとめました。

# 手順
Djangoには組み込みで認証機能が備わっており、今回はそれを利用して実装する

## 1. ユーザー認証のためのセットアップ
### 1.1 必要なアプリケーションのインストール
Djangoプロジェクトのsettings.pyファイルに、以下のアプリケーションを追加します。

```python:settings.py
INSTALLED_APPS = [
    ...
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    ...
]
```
### 1.2 URLの設定
urls.pyファイルに以下のURLパターンを追加します。

```python:urls.py
from django.contrib.auth import views as auth_views

urlpatterns = [
    ...
    path('login/', auth_views.LoginView.as_view(), name='login'),
    path('logout/', auth_views.LogoutView.as_view(), name='logout'),
    ...
]
```

## 2. LoginViewを実装する
views.pyにLoginViewを実装しパスを通す。
```python:views.py
from django.contrib.auth.views import LoginView

class AppLoginView(LoginView):
    fields = "__all__"
    #初期時はregistrationディレクトリが指定されている、自作のアプリ内に変更したい場合は以下が必要
    #template_name = "myApp/login.html"    "myApp"は各自のapp名に置き換え

    def get_success_url(self):
        return reverse_lazy("遷移先url")    #"http://127.0.0.1:8000/〇〇/"の"〇〇"部
```
```python:urls.py
from views import ApploginView
from django.contrib.auth.views import LogoutView

urlpatterns = [
    path("login/", AppLoginView.as_view(), name="login"),
    path("logout/", AppLoginView.as_view(next_page="login"), name="logout"),
]
```
次に`ligin.html`を作成していきます。
`templateディレクトリ`内の`registrationディレクトリ`（初期時）もしくは自作`appディレクトリ`内に`login.html`を作成します。
```html:login.html
<h2>Login</h2>
<form method="post">
  {% csrf_token %}
  {{ form.as_p }}
  <button type="submit">Login</button>
</form>
```
ログアウトはログアウトボタンを配置したいhtml内に以下を記載してください。
```html
<a href="{% url 'logout' %}">ログアウト</a>
```

これで最低限のログイン機能は実装できます。


## 3. ログインしている場合としていない場合での表示内容の切り替え
template内で切り替える方法とviewで切り替える方法の２通りがあります。

### template内で切り替える方法
Djangoでは、テンプレート内でuser変数を使用することでログイン中のユーザー情報にアクセスできます。以下のように`{% if user.is_authenticated %}`を使用して、ログインしているかどうかをチェックすることができます。

```html:my_template.html
{% if request.user.is_authenticated %}
    <p>Welcome, {{ request.user.username }}!</p>
    <!-- ログインしているユーザー向けのコンテンツ -->
{% else %}
    <p>Please log in.</p>
    <!-- ログインしていないユーザー向けのコンテンツ -->
{% endif %}
```

### view内で切り替える方法
ビュー内でユーザーごとに異なる情報を処理することもできます。以下の例は、ログインしているユーザーに応じたデータをテンプレートに渡す方法を示しています。

```python:views.py
from django.shortcuts import render

def my_view(request):
    if request.user.is_authenticated:
        # ログインしているユーザー向けのデータを取得
        data = get_data_for_user(request.user)
    else:
        # ログインしていないユーザー向けのデータを設定
        data = get_default_data()

    return render(request, 'my_template.html', {'data': data})
```

## ログインしていない場合ログインページに飛ばしたい場合
公式でLoginRequiredMixinという機能が用意されている。
### 適応したいクラスの第一引数に`LoginRequiredMixin`を追加する。
```python:views.py
from django.views.generic import ListView
from django.contrib.auth.mixins import LoginRequiredMixin

class HomePage(LoginRequiredMixin, ListView):    #ListViewは各自のViewに変更
    model = "各自の使用したいモデルを指定"
    ...
```
### `settings.py`内に飛ばしたい先の指定を記載する。
デフォルトでは `/accounts/login/` に設定されています。
名前付きURLパターンを使用することもできます。例: LOGIN_URL = 'login'
```python:settings.py
...

LOGIN_URL = "myapp_app:login"
```

## 4. ログインしているユーザ毎に表示内容を切り替える
get_context_data関数をオーバライドしてログインしているユーザのデータのみにフィルタをかける。
※今回の例ではフィルタをかけたいモデルにあらかじめuserの項目があることを前提としています。

```python:views.py
from django.shortcuts import render

def my_view(request):
    ...
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["template内でのkey"] = context["key"].filter(user=self.request.user)    #ログインしているユーザのデータにフィルタをかける
        return context
```

## 備考①：request.userとuserの違い
Djangoテンプレートで使用される `{{ user }}` と `{{ request.user }}` の違いは、主にコンテキストに渡されるユーザーオブジェクトの指定方法に依存します。

1. **`{{ user }}`**: この変数は、ビューからテンプレートに明示的に渡されたユーザーオブジェクトを参照します。ビュー関数内で、`context['user'] = some_user_object` のように設定されている場合に使用されます。

2. **`{{ request.user }}`**: この変数は、リクエストオブジェクトから現在の認証済みユーザーを参照します。Djangoの認証システムを使用している場合、ログイン中のユーザーが `request.user` に自動的に格納されます。テンプレート内でこの変数を使用すると、現在のリクエストに関連付けられた認証済みユーザーを取得することができます。

基本的に、`{{ request.user }}` を使用すると、認証に基づいて自動的に現在のユーザーを取得することができるため、多くの場合これが便利です。一方、`{{ user }}` は、特定のビューで特別なユーザーオブジェクトを渡す必要がある場合など、よりカスタマイズされたユースケースで使用することができます。

## 備考②：LOGIN_URLとLOGIN_REDIRECT_URLの違い
### LOGIN_URL
この設定は、ログインが必要なビューに未認証のユーザーがアクセスしたときにリダイレクトされるURLを指定します。
デフォルトでは /accounts/login/ に設定されています。
名前付きURLパターンを使用することもできます。例: LOGIN_URL = 'login'
未認証ユーザーが @login_required デコレータや LoginRequiredMixin などのログイン保護を受けたビューにアクセスした場合、ユーザーはこのURLにリダイレクトされます。

### LOGIN_REDIRECT_URL
この設定は、ユーザーがログインした後にリダイレクトされるURLを指定します。
デフォルトでは /accounts/profile/ に設定されています。
名前付きURLパターンを使用することもできます。例: LOGIN_REDIRECT_URL = 'dashboard'
例えば、ユーザーがログインフォームを正常に送信した後、この設定で指定されたURLにリダイレクトされます。

# まとめ
組み込みの認証機能を使用することで簡単にログイン機能を実装することができる