Home > Enterprise >  Django Test Client sends values as a list instead of strings
Django Test Client sends values as a list instead of strings

Time:10-13

I have a problem, I am not sure whether I had overlooked something, or simply doing something wrong. I am trying to test an endpoint that allows a user to register.

My model:

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")

Serializer:

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

View:

@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)

And I am trying to use a Django test client in a following way:

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))

The function works as expected, but a strange thing happens. When I inspect request.POST both username and password are a list as so:

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

I am puzzled as I dont understand what causes this behavior.

CodePudding user response:

This is a function built-in to Django to handle multiple values with the same key. See docs.

And when you are using Django's test client this.c.post and send data in the second parameter. Then Django will send that as URL parameters and not a POST body.

So your request will look like: '/api/register/?username=test_user&password=1234'

Let's say you send a request with '/api/register/?username=test_user&password=1234&password=5486' Then your request.POST would look like:

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

So, I don't think you have to worry about this.

CodePudding user response:

As mentioned in Django's doc:

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.

Then, if you want to access those items, use QueryDict.__getitem__ method:

So, in your case, it would be something like:

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

Or if you have multiple values on the queryparams, you can use QueryDict.getlist:

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

QueryDict: 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

CodePudding user response:

I think that's the normal behavior of the request.POST. As you can do a POST with multiple values for the same parameter: Eg: /api/register/?username=user&username=admin, so you will have

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

You can check this in the official doc: https://docs.djangoproject.com/en/3.2/ref/request-response/#django.http.QueryDict

  • Related