You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 23:35:29