Skip to content
Advertisement

How can I pass in url arguments to an APIRequestFactory put request?

I have been trying for hours but cannot figure out how to pass a url argument through an APIRequestFactory put request. I have tried it through Postman when running my server and the url variable is passed just fine, but when I run it in my tests it stops working.

What I mean is that when I send a Postman PUT request to ‘/litter/1/’ it will successfully take in the 1 as the variable litterId since my url is setup like this

path('litter/', include('apps.litter.urls')),

and

path('<int:litterId>/', LitterView.as_view(), name='litter-with-id')

But when I try and send an APIRequestFactory put request to that same url, for some reason the 1 will not go through as the litterId anymore.

Some relevant pieces of code…

My top level url.py

from rest_framework.authtoken import views

from apps.litter.views import LitterView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('auth/', include('apps.my_auth.urls')),
    path('litter/', include('apps.litter.urls')),
]

This is my app specific urls.py

from .views import LitterView

urlpatterns = [
    path('', LitterView.as_view(), name='standard-litter'),
    path('<int:litterId>/', LitterView.as_view(), name='litter-with-id'),
]

Here is my views.py

import json
  
from django.contrib.auth.models import User
from django.db import IntegrityError
from django.views.decorators.csrf import csrf_exempt
from rest_framework import authentication, permissions
from rest_framework.parsers import JSONParser
from rest_framework.permissions import IsAuthenticated
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from rest_framework.views import APIView
from django.db import models

from .models import Litter
from .serializers import LitterSerializer


#@csrf_exempt
class LitterView(APIView):
    """ 
    View for litter related requests

    * Requres token auth
    """
    permission_classes = (IsAuthenticated,)
    authentication_classes = [authentication.TokenAuthentication]
    renderer_classes = [JSONRenderer]

    def put(self, request, litterId=0):
        """
        Updates an old litter
        """

        try:
            litterModel = Litter.objects.get(user=request.user, id=litterId)
        except Litter.DoesNotExist:
            returnData = {'status': 'fail',
                          'error': 'Could not find object with that id.'}
            return Response(returnData)

        serializer_class = LitterSerializer
        serialized = LitterSerializer(litterModel, data=request.data)

        if serialized.is_valid():
            litterModel = serialized.save()

            returnData = {'status': 'okay',
                          'litter': [serialized.data]}
            return Response(returnData)
        else:
            return Response(serialized.errors, status=400)

And here is the relevant test.

def test_easy_successful_put_type(self):
        """
        Testing a simple put
        """

        user = UserFactory()
        amount = 40
        amountChange = 20
        litter = LitterFactory(user=user, amount=amount)

        data = {'typeOfLitter': litter.typeOfLitter,
                'amount': litter.amount + amountChange,
                'timeCollected': litter.timeCollected}

        url = '/litter/' + str(litter.id) + '/'
        request = self.factory.put(url, data, format='json') 
        force_authenticate(request, user=user)
        view = LitterView.as_view()
        response = view(request).render()
        responseData = json.loads(response.content)

No matter what I do, I cannot get the int:litterId to get passed in, the put function always has the default value of 0. Any help would be greatly appreciated.

Advertisement

Answer

Your problem is here:

response = view(request).render()

You are manually passing the request to the view, also not passing the kwarg litterId, instead use APIClient and make a put request to the url. First import the required modules:

from django.urls import reverse
from rest_framework.test import APIClient

then:

user = UserFactory()
amount = 40
amountChange = 20
litter = LitterFactory(user=user, amount=amount)
data = {
            'typeOfLitter': litter.typeOfLitter,
            'amount': litter.amount + amountChange,
            'timeCollected': litter.timeCollected
       }

url = reverse('litter-with-id', kwargs={'litterId': litter.id})
client = APIClient()
client.force_authenticate(user=user)
response = client.put(url, data, format='json')

Advertisement