如何让force_authenticate设置的request.user在Django中间件执行时生效?
问题描述
我在使用APITestCase及自带的self.client编写测试时,通过self.client.force_authenticate认证用户。发现该操作由rest_framework.request.Request构造函数处理,而该构造函数在rest_framework/views.py(391)的initialize_request()中创建,执行时机晚于中间件。
在中间件断点处调试可见:
(Pdb) p request <WSGIRequest: GET '/api/admin/products/'> (Pdb) p request.user <SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x7f537e0aa9a0>> (Pdb) p request._force_auth_user <User: admin>
DRF设置的._force_auth_user已存在,但request.user尚未生效。想找简洁方法让force_authenticate设置的request.user在中间件执行时(至少在AuthenticationMiddleware之后)可用。
解决方法
- 方法一:测试用例内手动同步
在测试中调用force_authenticate后,直接将用户同步到客户端的原生请求对象上:
user = User.objects.get(username="admin") self.client.force_authenticate(user=user) # 同步用户到Django原生WSGIRequest的user属性 self.client.request.user = user # 发起请求 response = self.client.get("/api/admin/products/")
这种方式适合单个测试的临时处理,无需修改全局配置。
- 方法二:自定义中间件全局处理
编写一个轻量中间件,放在AuthenticationMiddleware之后、DRF认证中间件之前,自动检测并同步_force_auth_user到request.user:
# 项目下的middleware.py文件 class ForceAuthSyncMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): if hasattr(request, "_force_auth_user"): # 替换原生request.user为DRF预设置的用户 request.user = request._force_auth_user # 删除标记避免DRF后续重复处理 delattr(request, "_force_auth_user") response = self.get_response(request) return response
在settings.py的MIDDLEWARE列表中添加该中间件,注意位置顺序:
MIDDLEWARE = [ # ... 其他系统中间件 "django.contrib.auth.middleware.AuthenticationMiddleware", # 必须放在AuthenticationMiddleware之后 "your_app_name.middleware.ForceAuthSyncMiddleware", # DRF的认证中间件放在后面 "rest_framework.middleware.AuthenticationMiddleware", # ... 其他自定义中间件 ]
这种方式一劳永逸,所有测试和正常请求都会自动完成用户同步。
内容的提问来源于stack exchange,提问作者Julien Palard
相关产品推荐
相关产品推荐

