DRF、django-rest-frameworkが入っている状況で、Djangoのミドルウェアを使って、ログインユーザに対してなにかをしたいな〜と思っているのだが、何度やってもAnonymousUserになってしまう。
MIDDLEWARE = [
...
'django.contrib.auth.middleware.AuthenticationMiddleware',
'foo.bar.SomethingMiddleware',
...
]
class SomethingMiddleware(MiddlewareMixin):
def process_request(self, request):
print(request.user)
ソースを読んでいくと、どうやら、DRFの認証と、Djangoの認証処理の違いがあり、具体的には…
- Django
- https://docs.djangoproject.com/ja/5.1/topics/auth/default/#auth-web-requests
- AuthenticationMiddlewareはセッションがあればUserが設定される、なければAnonymousUserになる
- loginでセッションを設定できる
- DRF
- https://www.django-rest-framework.org/api-guide/authentication/
- DEFAULT_AUTHENTICATION_CLASSES でどんな認証をするか設定できる
- Djangoでビューの処理に入ってからはすべてDRF側で処理されるようになっていて、リクエストオブジェクトもDRFのものが使われる
- このあたりから追っていける https://github.com/encode/django-rest-framework/blob/3.15.2/rest_framework/views.py#L492
- Djangoのミドルウェアの処理でこの認証結果を取ることができない
- process_request -> process_view -> View -> process_template_response という流れのため
つまり、ミドルウェアでrequest.userを取ろうと思っても、DRFが入っている状況では無理、ということになる。
深堀りするきっかけとなったQA: python - Django and Middleware which uses request.user is always Anonymous - Stack Overflow
機能追加せんかね?というディスカッションもあった。ここにリクエストライフサイクルのめっちゃわかりやすい図がある: DRF Middleware proposal · encode/django-rest-framework · Discussion #7770
今回の実装では、ログインユーザのデータに基づいてリクエストを拒否しようかどうしようか、というものだったので、それってパーミッションだよね、ということでpermissionsとして実装することで事なきを得た。
ところでこれって、もともとの実装で django.contrib.auth.middleware.AuthenticationMiddleware
がミドルウェアに入っていることが混乱の種なのでは???という気がする。が、実はどこかでは使っている可能性もあり、迂闊にはずすわけにもいかないので、持ち帰りで議論へ…
パーミッションにするにしても全部のエンドポイントに適用したいので、DEFAULT_PERMISSION_CLASSES を設定しようにも、実装済みのビューでは、permission_classes を設定しているところがあって、これって上書きされちゃうのか〜〜とまたトラップを踏んだ。
https://github.com/encode/django-rest-framework/blob/3.15.2/rest_framework/views.py#L111