Django 测试客户端将值作为列表而不是字符串发送

Django Test Client sends values as a list instead of strings

我有一个问题,我不确定是我忽略了什么,还是只是做错了什么。我正在尝试测试允许用户注册的端点。

我的模特:

class Account(User):
    objects = UserManager()
    balance = models.FloatField(blank=True, default=0)
    rank_position = models.IntegerField(blank=True, null=True)
    rank_title = models.CharField(max_length=255, blank=True, default="Novice")

序列化器:

class AccountSerializer(ModelSerializer):
    class Meta:
        model = Account
        fields = '__all__

查看:

@api_view(['POST'])
def register(request):
    try:
        acc = Account.objects.create(**request.POST)
        acc_srl = AccountSerializer(acc)
        return Response(data=acc_srl.data, status=status.HTTP_201_CREATED)
    except Exception as e:
        return Response(status=status.HTTP_400_BAD_REQUEST)

我正在尝试通过以下方式使用 Django 测试客户端:

class TestAuthentication(TestCase):
    def setUp(self):
        self.c = Client()

    def test_register(self):
        data = {'username': 'test_user', 'password': '1234'}
        response = self.c.post('/api/register/', data)
        print(response.json())
        self.assertEqual(response.status_code, 201)
        acc = Account.objects.get(username="test_user")
        self.assertEqual(acc.username, "test_user")
        self.assertTrue(isinstance(acc, User))

函数按预期运行,但奇怪的事情发生了。当我检查 request.POST 时,用户名和密码都是一个列表:

<QueryDict: {'username': ['test_user'], 'password': ['1234']}>

我很困惑,因为我不明白是什么导致了这种行为。

这是 Django 的内置函数,用于处理具有相同键的多个值。参见 docs

而当你使用 Django 的测试客户端时 this.c.post 并在第二个参数中发送数据。然后 Django 会将其作为 URL 参数而不是 POST 主体发送。

因此您的请求将如下所示:'/api/register/?username=test_user&password=1234'

假设您使用 '/api/register/?username=test_user&password=1234&password=5486' 发送请求 然后你的 request.POST 看起来像:

<QueryDict: {'username': ['test_user'], 'password': ['1234', '5486']}>

所以,我认为您不必为此担心。

我认为这是 request.POST 的正常行为。因为您可以为同一参数执行具有多个值的 POST:例如:/api/register/?username=user&username=admin,所以您将拥有

<QueryDict: {'username': ['user', 'admin']}>

你可以在官方文档中查看:https://docs.djangoproject.com/en/3.2/ref/request-response/#django.http.QueryDict

如 Django 文档中所述:

In an HttpRequest object, the GET and POST attributes are instances of django.http.QueryDict, a dictionary-like class customized to deal with multiple values for the same key.

然后,如果您想访问这些项目,请使用 QueryDict.__getitem__ 方法:

所以,在你的情况下,它会是这样的:

>>> request.POST['username']
test_user
>>> request.POST['password']
1234

或者如果您在查询参数上有多个值,您可以使用 QueryDict.getlist:

>>> POST = QueryDict('user_ids=ID1&user_ids=ID2&user_ids=ID3&')
>>> POST.getlist('user_ids')
['ID1', 'ID2', 'ID3']

查询字典:https://docs.djangoproject.com/en/3.1/ref/request-response/#querydict-objects

QueryDict.__getitem__: https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.QueryDict.\_\_getitem\_\_

QueryDict.getlist: https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.QueryDict.getlist