Is it possible to add server response code to JsonResponse ? I need server to reply with 404 in some circumstances.
I have following view
def CreateOrAuth(request):
try:
username = request.POST.get("username")
queryset = User.objects.get(username=username)
except Exception as e:
return JsonResponse({'status': 'user with {} not exist'.format(username)})
And I want to add 404 server code here
Yes, you can. Just pass additional parameter status to JsonResponse:
return JsonResponse({'status': 'user with {} not exist'.format(username)}, status=404)
Related
I have created a DRF api authenticated with jwt,the token is stored in a cookie.I can successfully access all the viewsets using the token with postman.It only becomes a problem when l want to pass the token to angular frontend for the same operations.I am using django rest framework backend and Angular 9 frontend.Also note that l am storing the token in a cookie.
My views.py
class LoginView(APIView):
def post(self,request):
#getting the inputs from frontend/postman
email =request.data['email']
password =request.data['password']
user=User.objects.filter(email=email).first()
#Authentication
if user is None:
raise AuthenticationFailed('User not found!')
if user.password!=password :
raise AuthenticationFailed("incorrect password")
payload = {
'id':user.id,
'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=10),
'iat': datetime.datetime.utcnow()
}
token = jwt.encode(payload, 'secret', algorithm='HS256')
response = Response()
#storing the token in a cookie
response.set_cookie(key='jwt',value=token ,httponly=True)
response.data = {
'jwt':token
}
return response
class UserView(APIView):
def get(self,request):
token=request.COOKIES.get('jwt')
if not token:
raise AuthenticationFailed("unauthorised")
try:
payload =jwt.decode(token, 'secret', algorithms=['HS256'])
except jwt.ExpiredSignatureError:
raise AuthenticationFailed("session expired")
user=User.objects.get(id=payload['id'])
serializer=UserSerializer(user)
return Response(serializer.data)
class Update(APIView):
def get_object(self,request):
try:
token=request.COOKIES.get('jwt')
if not token:
raise AuthenticationFailed("unauthorised")
try:
payload =jwt.decode(token, 'secret', algorithms=['HS256'])
except jwt.ExpiredSignatureError:
raise AuthenticationFailed("session expired")
user=User.objects.get(id=payload['id'])
return user
except User.DoesNotExist:
return Response("wakadhakwa",status=status.HTTP_204_NO_CONTENT)
def get(self,request):
obj=self.get_object(request)
serializer=UserSerializer(obj)
return Response(serializer.data)
def put(self,request):
obj=self.get_object(request)
serializer=UserSerializer(obj,data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response("corrupted data",status=status.HTTP_204_NO_CONTENT)
def delete(self,request):
all=self.get_object(request)
all.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
Did you check that the cookie gets properly saved in browser when receiving response from login?
Are you calling the UserView endpoints from your Angular app with an AJAX call or are you reloading the page? If it is a call from the app make sure that the request sends cookies. It depends on how exactly you request the data, e.g. if you're using fetch, then make sure you have the option credentials: 'include' set. If you're requesting the data in some other way try to find in the documentation which option is used to enable sending credentials (cookies).
Here I am writing test for function delete_position but I got little problem here.I am getting AssertionError: 302 != 200 which is I think because I have not send delete_single name in client.post but it is defined in my views.
How can I check if delete_single is in request.POST in my test_delete_position ?
views.py
def delete_position(request, pk):
position = get_object_or_404(Position, pk=pk)
if request.method == 'POST' and 'delete_single' in request.POST:
position.delete()
messages.success(request, '{} deleted.'.format(position.title))
return redirect('organization:view_positions')
else:
messages.error(request, 'Sorry.Invalid request.')
tests.py
class PositionTestCase(TestCase):
def setUp(self):
# create admin user for authentication
self.admin = get_user_model().objects.create_superuser(username='admin', password='password#123',email='admin#admin.com')
self.client = Client()
self.position = Position.objects.create(title='Sr.Python Developer')
def test_delete_position(self):
self.client.login(username='admin', password='password#123')
response = self.client.post(reverse('organization:delete_position', kwargs={'pk': self.position.pk}))
print(response)
self.assertEqual(response.status_code, 200)
Here I am writing test for function delete_position but I got little problem here.I am getting AssertionError: 302 != 200 which is I think because I have not send delete_single name in client.post but it is defined in my views.
A 302 is expected behavior in case of a succesful POST. This is the Post/Redirect/Get pattern [wiki]. In case the POST was successful, then a 200 means that if the user refreshes that page, he/she will make the same POST request again. In order to prevent that, usually it will return a redirect (HTTP status 302), and the browser will then make a GET request to fetch the object.
It is thus better to just change your tests, and check if it returns a 302.
How to send customised response if the unauthorised credentials were provided in django rest.
class StockList(APIView):
permission_classes = [IsAuthenticated]
def get(self,request):
stocks = Stock.objects.all()
serializer = StockSerializer(stocks,many=True)
return Response({'user': serializer.data,'post': serializer.data})
def post(self):
pass
Here when I Hit url by invalid credentials i get 401 error on development server.
But i want to send customised response on client using json.
any suggestions are welcomed.
Thank you.
You can use a custom exception handler to customized response of api exception.
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context):
# Call REST framework's default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
# Now add the HTTP status code to the response.
if response is not None:
response.data['status_code'] = response.status_code
if response.status_code == 401:
response.data['some_message'] = "Some custom message"
return response
Then in setting.py you have to add your custom error handler
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'my_project.path.to.custom_exception_handler'
}
Refs: docs
I need to test if redirecting to a url will result in a 404 error.
Following example in django docs: https://docs.djangoproject.com/en/1.8/ref/urlresolvers/#resolve
I managed to get the following to work. So if the redirect_url raises a 404, we redirect the user to homepage.
redirect_url = '/blog/hello-world'
view, args, kwargs = resolve()
kwargs['request'] = request
try:
view(*args, **kwargs)
except Http404:
self.message_user(...)
return HttpResponseRedirect('/')
However I need to test if the redirect_url with a GET parameter of ?edit also raise 404. As the 'edit' flag is used to return a different queryset. e.g.
if 'edit' in request.GET:
qs = qs.drafts()
else:
qs = qs.public()
And I changed my original code:
redirect_url = '%s?edit' % redirect_url
[...]
However this raise Resolver404.
Full stacktrace here: http://dpaste.com/1DQHH7Q
Now my question is how can I test for HTTP404 error with GET parameters?
The path blog/2015/07/14/sky-limit-only-those-who-arent-afraid-fly/?edit is valid, when I go the url in the browser.. it works as expected.
A dirty solution is to do this:
from django.test.client import Client
client = Client()
resp = client.get(redirect_url)
if not resp.status_code == 404:
return HttpResponseRedirect(redirect_url)
I really don't want to use this.
The resolve() method takes the url without the GET parameters. You could try replacing request.GET.
redirect_url = '/blog/hello-world'
view, args, kwargs = resolve()
request.GET = {'edit': ''}
kwargs['request'] = request
try:
view(*args, **kwargs)
except Http404:
self.message_user(...)
return HttpResponseRedirect('/')
Note that request is the request object for your current view, so be careful be careful if you need the actual request.GET data.
I have a Test Class, that test the access of all page with different user.
Those access are defined by decorator on each of my views.
views.py :
#login_required
def afficher(request):
...
...
#creation_permission_required
def ajouter(request):
...
...
Some of these decorator are defined by me.
decorators.py :
def creation_permission_required(function):
#wraps(function)
#login_required
def decorateur(request, *k, **a):
user = get_object_or_404(User, username__iexact=request.user.username)
if user.is_superuser or user.get_profile().creation:
return function(request, *k, **a)
else:
return HttpResponseRedirect(reverse("non_autorise"))# <--- PROBLEM
return decorateur
return function
When I test them, I use the status_code attribute to verify if the user can access or not the page
test.py :
c = Client()
c.login(username='aucun', password='aucun')
for url in self.url_aucun:
r = c.get(reverse(url['url'], args=url['args']))
self.assertEqual(r.status_code, 200)
for url in self.url_creation:
r = c.get(reverse(url['url'], args=url['args']))
self.assertEqual(r.status_code, 302) # <--- SECOND PROBLEM
When a user doesn't have the right to access a page, the page should return a 403 error (forbidden). How can I do to test 403 instead of 302 ?
EDIT : I tried to use HttpResponseForbidden(reverse("non_autorise")), but couldn't get any content. So then I tried to make my own HttpResponse which is an exact copy of HttpResponseRedirect but with another status_code (403) still didn't get any content...
decorators.py :
class HttpResponseTest(HttpResponse):
def __init__(self, redirect_to):
super(HttpResponseTest, self).__init__()
self['Location'] = iri_to_uri(redirect_to)
self.status_code = 403
def creation_permission_required(function):
#wraps(function)
#login_required
def decorateur(request, *k, **a):
user = get_object_or_404(User, username__iexact=request.user.username)
if user.is_superuser or user.get_profile().creation:
return function(request, *k, **a)
else:
return HttpResponseTest(reverse("non_autorise"))# <--- PROBLEM
return decorateur
return function
If you want a 403 response, you can raise a PermissionDenied exception in your decorator if you are using Django 1.4. Alternatively, you can return a HttpResponseForbidden in your decorator. You will also have to build a custom login_required decorator.
self.assertEqual(r.status_code, 403)
I had this same issue and solved it by instructing the test get() to follow the redirect using follow=True. Using BlueMagma's example it would look something like this:
for url in self.url_creation:
r = c.get(reverse(url['url'], args=url['args']), follow=True)
self.assertEqual(r.status_code, 403) # <--- SECOND PROBLEM NO MORE!!!
Hope this helps someone else!