Djangoでアクセス制限を行う（AccessMixinクラス）

# 結論
- ユーザのアクセス制限を行うLoginRequiredMixin, UserPassesTestMixinクラスを紹介。
- 両クラス共通で、AccessMixinクラスを継承している。
- 基本的に両クラス、制限をかけたいビュークラスに継承するだけでそのビューに対してアクセス制限をかけることができる。
- 継承する際は、LoginRequiredMixinが一番左、UserPassesTestMixinがその次に指定する必要がある。そうでない場合は、エラー。
- これらのクラスを使用するには、[ログイン・ログアウト機能を作成](https://qiita.com/kkk777/items/0a81139704f3b10d072e#%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3%E3%83%AD%E3%82%B0%E3%82%A2%E3%82%A6%E3%83%88%E6%A9%9F%E8%83%BD%E3%81%AE%E5%AE%9F%E8%A3%85)しておく必要がある
- LoginRequiredMixinでできることは、UserPassesTestMixinでもできる。強化版の印象。

|アクセス制限クラス|用途|
|-|-|
|LoginRequiredMixin|ログインしていないユーザからのアクセスに制限をかけたい場合に使用する|
|UserPassesTestMixin|ログイン制限をかけた上で、独自のアクセス制限を追加で設定したい場合に使用する|

# LoginRequiredMixin
**環境変数を設定するだけで使用可能**

- 定義の順序:
    - settinga.pyで、リダイレクトに関する`LOGIN_URL, LOGIN_REDIRECT_URL, LOGOUT_REDIRECT_URL`3つの環境変数を設定する
    - クラスをimport
    - アクセス制限をかけたい対象のビュークラスに継承させる

|親のAccessMixinクラスの属性とメソッド|説明|
|-|-|
|raise_exception|ログインしていないユーザがアクセスした場合、403ページにリダイレクトさせるのかを指定する。Falseがデフォルト。|
|handle_no_permission()|raise_exceptionの値でリダイレクト先を決めるメソッド。raise_exception = False（デフォルト）の場合、リダイレクト先を指定する（デフォルトでsettings.LOGIN_URLにリダイレクト）。Trueの場合、403ページにリダイレクトさせる。|
- **ただし、ログイン済みのユーザであれば、reise_exceptionの値がFalseでも403エラーを返す。**
    - ログイン済みの状態なので、ログインページにリダイレクトさせないため。

※継承する際は引数の最初に記述すること。でないとエラー。
※上記の3つの環境変数を設定し、ビュークラスに継承するだけで、ログイン制限をかけることができる。変数やメソッドをオーバーライドする必要はない。

## 具体例
```python:views.py
from django.views.generic import ListView
from django.contrib.auth.mixins import LoginRequiredMixin

from .models import Post

class Index(LoginRequiredMixin, ListView):
    template_name = 'report/index.html'
    model = Post
    ordering = 'created' # 新規作成順　'-createdで降順'
```
- LoginRequiredMixinを継承するだけで、対象のビューにログインのアクセス制限がかかる
    - つまり、ログイン済みのユーザからのリクエストがないとこのビューを実行しない


# UserPassesTestMixin

- 定義の順序:
    - クラスをimport
    - 共通化させるクラスを作成（以下の具体例では、OwnerOnlyクラス）
    - test_func()の定義（Trueでアクセス可、Falseでアクセス不可）
    - test_func()の戻り値がFalse用にリダイレクト先を指定（handle_no_permission()）

|親のAccessMixinクラスの属性とメソッド|説明|
|-|-|
|raise_exception|ログインしていないユーザがアクセスした場合、403ページにリダイレクトさせるのかを指定する。Falseがデフォルト。|
|handle_no_permission()|raise_exceptionの値でリダイレクト先を決めるメソッド。raise_exception = False（デフォルト）の場合、リダイレクト先を指定する（デフォルトでsettings.LOGIN_URLにリダイレクト）。Trueの場合、403ページにリダイレクトさせる。|
|test_func()|ログインに成功したユーザに対して、独自のアクセス制限を設定する。戻り値がTrueの場合、サイトへのアクセスを許可する。Falseの場合、アクセスを拒否し、403ページへリダイレクトさせる。|
- **ただし、ログイン済みのユーザであれば、reise_exceptionの値がFalseでも403エラーを返す。**
    - ログイン済みの状態なので、ログインページにリダイレクトさせないため。



※LoginRequiredMixinと同様、継承する際は引数の最初に記述すること。でないとエラー。

## 具体例
### ①独自制限を共通化させたい場合

```python:views.py
from django.contrib.auth.mixins import UserPassesTestMixin
from django.shortcuts import redirect

class OwnerOnly(UserPassesTestMixin):
    def test_func(self):
        object = self.get_object()
        return object.user == self.request.user
    
    def handle_no_permission(self):
        return redirect("Detail", pk=self.kwargs["pk"])
```
- 他のビュークラスでも独自の設定を共通化させるために、UserPassesTestMixinクラスを継承したOwnerOnlyというクラスを定義している。
- test_func関数では、継承させる予定のUpdateViewで取得した１件のレコードのカラム(user)の値とログイン中のユーザーが一致している場合は、Trueを返す。
- [test_func関数の戻り値がFalseの場合、handle_no_permission関数を実行する。](https://github.com/dbrgn/django-auth-mixins/blob/c2ea637888e4eea822f53a40fba6188c50220ab5/auth_mixins/__init__.py#L112)
- handle_no_permission関数によって、詳細ページへリダイレクトする。


※UpdateViewに継承することを前提に定義している（UpdateViewで使用できる属性、メソッドも定義している）

### ②独自制限を１つのビューに設定したい場合
```python:views.py
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.mixins import UserPassesTestMixin
from django.views.generic import DetailView

class Detail(LoginRequiredMixin, UserPassesTestMixin, DetailView):
    template_name = 'report/detaile.html'
    model = Post

    def test_func(self):
        # 現在ログイン中ユーザのpkとURLのpkパラメータが同じ、またはsuperuserならTrue
        current_user = self.request.user
        return current_user.pk == self.kwargs['pk'] or current_user.is_superuser
```
- 継承時は、LoginRequiredMixinが一番左
- 現在ログイン中のユーザは、self.request.userで取得
- self.kwargs['pk']によって、URLからpkパラメータの値を取得


※urls.pyで設定したURLに、pkパラメータがある前提
※ログイン機能を実装しておく必要がある