Django接口测试报错:无法将AnonymousUser赋值给Post.author字段
问题详情
我在编写接口测试用例时,创建帖子的测试用例持续失败,报错信息如下:
Cannot assign "<django.contrib.auth.models.AnonymousUser object at 0x7f81fe558fa0>": "Post.author" must be a "UserData" instance
测试代码(test.py)
from rest_framework.test import APITestCase, APIRequestFactory, APIClient from rest_framework import status from django.urls import reverse from .views import PostViewSet from django.contrib.auth import get_user_model User = get_user_model() class PostListCreateTestCase(APITestCase): def setUp(self): self.factory = APIRequestFactory() self.view = PostViewSet.as_view({"get": "list", "post": "create"}) self.url = reverse("post_list") self.user = User.objects.create_user( email="testuser@gmail.com", name="testuser" ) self.user.set_password("pass") self.user.save() def test_list_posts(self): request = self.factory.get(self.url) response = self.view(request) self.assertEqual(response.status_code, status.HTTP_200_OK) def test_create_post(self): print(User) print(self.user) client = APIClient() login = client.login(email="testuser@gmail.com", password="pass") self.assertTrue(login) sample_post = { "title": "sample title", "body": "sample body", } request = self.factory.post(self.url, sample_post) request.user = self.user print(isinstance(request.user, get_user_model())) response = self.view(request) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
PostViewSet视图代码
class PostViewSet(viewsets.ModelViewSet): serializer_class = PostSerializer queryset = Post.objects.all() def get_queryset(self): posts = Post.objects.all() return posts def get_object(self): post = get_object_or_404(self.get_queryset(), pk=self.kwargs["pk"]) self.check_object_permissions(self.request, post) return post def create(self, request, *args, **kwargs): try: post = Post.objects.create( title=request.data.get("title"), body=request.data.get("body"), author=request.user, ) post = PostSerializer(post) return Response(post.data, status=status.HTTP_201_CREATED) except Exception as ex: print(str(ex)) return Response(str(ex), status=status.HTTP_400_BAD_REQUEST) def list(self, request, *args, **kwargs): posts = self.get_queryset() serializer = self.get_serializer(posts, many=True) return Response( data=dict(posts=serializer.data, total=len(serializer.data)), status=status.HTTP_200_OK, )
测试运行结果
Found 2 test(s). Creating test database for alias 'default'... System check identified no issues (0 silenced). <class 'account.models.UserData'> testuser True Cannot assign "<django.contrib.auth.models.AnonymousUser object at 0x7f81fe558fa0>": "Post.author" must be a "UserData" instance. F. ====================================================================== FAIL: test_create_post (posts.tests.PostListCreateTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/amr/Snakat/social_network/posts/tests.py", line 41, in test_create_post self.assertEqual(response.status_code, status.HTTP_201_CREATED) AssertionError: 400 != 201 ---------------------------------------------------------------------- Ran 2 tests in 0.887s FAILED (failures=1) Destroying test database for alias 'default'...
解决方案
问题根源是混用了APIRequestFactory和APIClient,两个工具的认证状态不互通,导致视图获取到的仍是匿名用户。以下是两种修复方案:
方案1:只用APIRequestFactory,手动强制认证
修改test_create_post方法,移除APIClient相关代码,使用DRF的force_authenticate手动绑定用户:
# 先在测试文件顶部导入 from rest_framework.test import force_authenticate def test_create_post(self): print(User) print(self.user) sample_post = { "title": "sample title", "body": "sample body", } request = self.factory.post(self.url, sample_post) # 强制绑定当前用户到请求 force_authenticate(request, user=self.user) print(isinstance(request.user, get_user_model())) response = self.view(request) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
方案2:只用APIClient,直接发送请求
完全依赖APIClient处理认证和请求,无需手动操作request对象:
def test_create_post(self): print(User) print(self.user) client = APIClient() login = client.login(email="testuser@gmail.com", password="pass") self.assertTrue(login) sample_post = { "title": "sample title", "body": "sample body", } # 直接用客户端发送POST请求 response = client.post(self.url, sample_post) print(isinstance(client.handler._force_user, get_user_model())) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
额外优化建议
不要重写PostViewSet的create方法,改用序列化器自动处理用户关联更符合DRF规范:
# 在PostSerializer中添加 from rest_framework.serializers import CurrentUserDefault class PostSerializer(serializers.ModelSerializer): author = serializers.PrimaryKeyRelatedField( read_only=True, default=CurrentUserDefault() ) class Meta: model = Post fields = ['id', 'title', 'body', 'author']
之后删除PostViewSet中的create方法,让父类的默认逻辑自动处理对象创建,避免手动编码的错误。
内容的提问来源于stack exchange,提问作者Raskolnikov
相关产品推荐
相关产品推荐

