django test client.put doesn't have data - django

I am trying to test django view with REST framework.
It starts like this.
class MoveViewSet(viewsets.ModelViewSet):
serializer_class = FamilySerializer
queryset = Family.objects.all()
http_method_names = ['put']
def update(self, request, pk=None):
user = request.mobile_user
...
family_id = request.POST.get('family_id', None)
...
in test.py I make a request like this.
data = dict(
join_type='join',
family_id=target_family.id,
)
# also tried data = { ... }
header = {
'Authorization': user.api_key,
...
}
client = Client()
response = client.put(url, data=data, content_type='x-www-form-urlencoded', **header)
### What I've tried ###
# response = client.put(url, data=data, **header)
# response = self.client.put(url, data=data, **header)
# response = client.put(url, data=data, content_type='application/json', **header)
but in view, request.POST.get('paramname', 'default') makes error. request.POST has empty parameter set and of course, request.PUT is None.
I checked middleware but neither could I find params there.
Also I've tried this in middleware's process_request.
def process_request(self, request):
if request.method == "PUT" and request.content_type != "application/json":
if hasattr(request, '_post'):
del request._post
del request._files
try:
request.method = "POST"
request._load_post_and_files()
request.method = "PUT"
except AttributeError as e:
request.META['REQUEST_METHOD'] = 'POST'
request._load_post_and_files()
request.META['REQUEST_METHOD'] = 'PUT'
request.PUT = request.POST
It gives me this error.
AttributeError: 'WSGIRequest' object has no attribute 'content_type'
If I send client.post() with data, request.POST has data, but put doesn't.
How could I test client.put() with parameters?

request's content_type attribute has been added in Django 1.10. Since you are using Django 1.9 you cannot use this attribute. You can chec request.META.get('HTTP_ACCEPT') solution as mentioned here or update Django.

Related

How to make Post request by list action for create function in viewsets in DRF

I want to send post request from testing. Its working for postman but it didnot work for my test case. How can I give the data by post request.
Views.py,
class PersonalInfoAPI(viewsets.ViewSet):
permission_classes = [IsOwnerPermission]
def get_object(self, pk):
obj = get_object_or_404(Employee.objects.all(), pk=pk)
self.check_object_permissions(self.request, obj)
return obj
def create(self, request):
personal_info = JSONParser().parse(request)
.....
return ...
test.py
url = reverse('employee1-list')
self.client = Client(HTTP_AUTHORIZATION='Token ' + token.key)
resp1 = self.client.post(url, {"data": {
"appraisal_master_id": 1,
"personal_info": {
"employee_id": 1,
"experience": "abcd",
"education": "Nonoooo"
}
}
}, format='json')
print(resp1)
self.assertEqual(resp1.status_code, 200)
I have got 400 error. Please, tell anyone how can I pass data im properway,..
I got solution. When I gave request which passes dictionary data. then, I coverted into json its working fine.
url = reverse('employee1-list')
self.client = Client(HTTP_AUTHORIZATION='Token ' + token.key)
resp1 = self.client.post(url, data=data, content_type="application/json")
print(resp1)
self.assertEqual(resp1.status_code, 201)

Testing custom action on a viewset in Django Rest Framework

I have defined the following custome action for my ViewSet Agenda:
class AgendaViewSet(viewsets.ModelViewSet):
"""
A simple viewset to retrieve all the Agendas
"""
queryset = Agenda.objects.all()
serializer_class = AgendaSerializer
#action(detail=False, methods=['GET'])
def get_user_agenda(self, request, pk=None):
print('here1')
id = request.GET.get("id_user")
if not id:
return Response("No id in the request.", status=400)
id = int(id)
user = User.objects.filter(pk=id)
if not user:
return Response("No existant user with the given id.", status=400)
response = self.queryset.filter(UserRef__in=user)
if not response:
return Response("No existant Agenda.", status=400)
serializer = AgendaSerializer(response, many=True)
return Response(serializer.data)
Here, I'd like to unit-test my custom action named "get_user_agenda".
However, when I'm testing, the debug output("here1") doesn't show up, and it always returns 200 as a status_code.
Here's my test:
def test_GetUserAgenda(self):
request_url = f'Agenda/get_user_agenda/'
view = AgendaViewSet.as_view(actions={'get': 'retrieve'})
request = self.factory.get(request_url, {'id_user': 15})
response = view(request)
self.assertEqual(response.status_code, 400)
Note that:
self.factory = APIRequestFactory()
Am I missing something?
Sincerely,
You will have to use the method name of the custom action and not retrieve so:
view = AgendaViewSet.as_view(actions={'get': 'get_user_agenda'})
You have to specify request url
#action(detail=False, methods=['GET'], url_path='get_user_agenda')
def get_user_agenda(self, request, pk=None):
And in my opinion it would be better to use detail=True, and get pk from url.
For example: 'Agenda/pk_here/get_user_agenda/'

id parameter in get request

I am new to Django and a small detail has been bothering me.I have an api endpoint that returns the details of one patient. I have made a successful get request and tested on postman. It returns data for a particular patient with id = 996(I have hard coded the id). But I need to set it so it can pick the id from params in postman instead of the hard coded one here. How can I set params and append them on the url so that I use the id fed in postman instead of hard coding? Kindly assist
views.py
class PatientDetailsView(GenericAPIView):
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
#classmethod
#encryption_check
def get(self, request, *args, **kwargs):
try:
result = {}
auth = cc_authenticate()
res = getPatientDetails(auth["key"], id)
result = res
return Response(result, status=status.HTTP_200_OK)
except Exception as e:
error = getattr(e, "message", repr(e))
result["errors"] = error
result["status"] = "error"
return Response(result, status=status.HTTP_400_BAD_REQUEST)
api_service.py
def getPatientDetails(auth, id):
print("getting patientdetails from Callcenter")
try:
print(auth)
# print(url)
id= 996
headers = {
"Authorization": f'Token {auth}'
}
url = f'{CC_URL}/patients/v1/details/?id={id}'
print(url)
res = requests.get(url, headers=headers)
print("returning patientdetails response", res.status_code)
return res.json()
except ConnectionError as err:
print("connection exception occurred")
print(err)
return err
urls.py
path("details/", views.PatientDetailsView.as_view(), name="patient_info"),
This is the code I needed
id = request.GET.get('<id>')

Django Rest - testing api delete

Currrently I am testing my django rest api, but I am stuck on delete method.
My url looks like this
path('books/shelfs/<int:shelf>/readers/<int:pk>/',
views.ReaderViewSet.as_view(
{'get': 'retrieve', 'delete': 'destroy', 'patch': 'partial_update'}),
And My ViewSets looks like this
def destroy(self, request, pk=None, *args, **kwargs):
if request.data.get("book_type") is None:
raise ParseError(detail="book_type is required, options are : 'global, non-global'")
try:
instance = self.get_object()
user = self.request.user
serializer = self.get_serializer(self.get_object())
.......
self.perform_destroy(instance)
except Http404:
pass
return Response(status=status.HTTP_204_NO_CONTENT)
And my test case is this
def test_book_delete(self):
# check if delete works
book_type = {'book_type': 'global'}
response = self.client.delete("/api/v1/books/shelfs/{}/"
"readers/{}/".format(
1, 2), data=book_type)
self.assertEqual(response.status_code, 204)
But its alway 415 error
The question is, how to pass this book_type in delete ?
HTTP 415 means that the server refused to accept the JSON payload.
Docs:
If you need to explicitly encode the request body, you can do so by setting the content_type flag.
So try to set the content_type as follows:
response = self.client.delete("/api/v1/books/shelfs/{}/"
"readers/{}/".format(
1, 2), data=book_type, content_type="application/json")

django rest framework create json and csv api

i am trying to create 2 api's. 1 to access json using ajax datatable, and the other to create an export csv button.
the json api i manage to create and get the data from it using ajax, but i get problems when i try to access the api in the browser and the other to add the csv api..i will show you part of my scripts and the errors i get.
views.py
class VideoViewSet(viewsets.ModelViewSet):
queryset = DatamsVideos.objects.all()
serializer_class = VideosSerializer
parser_classes = (CSVParser,) + tuple(api_settings.DEFAULT_PARSER_CLASSES)
renderer_classes = (CSVRenderer,) + tuple(api_settings.DEFAULT_RENDERER_CLASSES)
def list(self, request, **kwargs):
try:
vs = query_vs_by_args(**request.query_params)
serializer = VideosSerializer(vs['items'], many=True)
result = dict()
result['data'] = serializer.data
result['draw'] = vs['draw']
result['recordsTotal'] = vs['total']
result['recordsFiltered'] = vs['count']
return Response(result, status=status.HTTP_200_OK, template_name=None, content_type=None)
except Exception as e:
return Response(e, status=status.HTTP_404_NOT_FOUND, template_name=None, content_type=None)
def get_renderer_context(self):
context = super(VideoViewSet, self).get_renderer_context()
context['header'] = (
self.request.GET['fields'].split(',')
if 'fields' in self.request.GET else None)
return context
#action(methods=['GET'], detail=False)
def bulk_upload(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_303_SEE_OTHER, headers={'Location': reverse('videos-api')})
urls.py
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'videos', VideoViewSet)
urlpatterns = [
url(r'^$', index, name='main'),
url(r'^api/', include(router.urls)),
]
and this is part of my models.py
def query_vs_by_args(**kwargs):
draw = int(kwargs.get('draw', None)[0])
length = int(kwargs.get('length', None)[0])
start = int(kwargs.get('start', None)[0])
search_value = kwargs.get('search[value]', None)[0]
order_column = kwargs.get('order[0][column]', None)[0]
order = kwargs.get('order[0][dir]', None)[0]
order_column = ORDER_VS_COLUMN_CHOICES[order_column]
# django orm '-' -> desc
if order == 'desc':
order_column = '-' + order_column
queryset = DatamsVideos.objects.all()
total = queryset.count()
if search_value:
queryset = queryset.filter(Q(link__icontains=search_value) |
Q(title__icontains=search_value) |
Q(views__icontains=search_value) | Q(date_added__icontains=search_value))
count = queryset.count()
queryset = queryset.order_by(order_column)[start:start + length]
return {
'items': queryset,
'count': count,
'total': total,
'draw': draw
}
1 of the problem i have is with the function list from views.py at
vs = query_vs_by_args(**request.query_params)
serializer = VideosSerializer(vs['items'], many=True)
i still manage to get the data from it using ajax, but when i try to access the api on the browser /main/api/videos/ it gives me this error:
HTTP 404 Not Found
Allow: GET, POST, HEAD, OPTIONS
Content-Type: text/csv ;utf-8
Vary: Accept
""
'NoneType' object has no attribute '__getitem__'
Same when i try to access /main/api/videos/?format=json
TypeError("'NoneType' object has no attribute '__getitem__'",) is not JSON serializable
When i try to get the csv from /main/api/videos/?format=csv, It Tells me File Not Found
I might be missing some details, can't just let me know if you want more information.
Thank you in advance
UPDATE
Now i just realize if i comment the function list the api's works fine..but my ajax is not working anymore because i don't have the 4 necessary things for to build the json
result['data'] = serializer.data
result['draw'] = ch['draw']
result['recordsTotal'] = ch['total']
result['recordsFiltered'] = ch['count']
I manage to fix the csv and api problem, by separating the classes.
class VideoCSVExportView(viewsets.ModelViewSet):
queryset = DatamsPornhubVideos.objects.all()
serializer_class = VideosSerializer
renderer_classes = (CSVRenderer,) + tuple(api_settings.DEFAULT_RENDERER_CLASSES)
after i create this new class for csv, i put it on a different route.
csv_router = DefaultRouter()
csv_router.register(r'videos', VideoCSVExportView)
...
url(r'^csv/', include(csv_router.urls)),
..