Django RestFramework JWT Token: Get User DoesNotExist error - django

After a user is deleted, the tokens on the client side are still valid until the time has expired. The issue is django restframwework does not handle a request from a deleted user and causes a 500. How can I prevent this?
aceback (most recent call last):
File "/lib/python3.6/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "/lib/python3.6/site-packages/django/core/handlers/base.py", line 179, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "/lib/python3.6/site-packages/django/views/generic/base.py", line 70, in view
return self.dispatch(request, *args, **kwargs)
File "/lib/python3.6/site-packages/rest_framework/views.py", line 505, in dispatch
response = self.handle_exception(exc)
File "/lib/python3.6/site-packages/rest_framework/views.py", line 465, in handle_exception
self.raise_uncaught_exception(exc)
File "/lib/python3.6/site-packages/rest_framework/views.py", line 476, in raise_uncaught_exception
raise exc
File "/lib/python3.6/site-packages/rest_framework/views.py", line 493, in dispatch
self.initial(request, *args, **kwargs)
File "/lib/python3.6/site-packages/rest_framework/views.py", line 410, in initial
self.perform_authentication(request)
File "/lib/python3.6/site-packages/rest_framework/views.py", line 324, in perform_authentication
request.user
File "/lib/python3.6/site-packages/rest_framework/request.py", line 220, in user
self._authenticate()
File "/lib/python3.6/site-packages/rest_framework/request.py", line 373, in _authenticate
user_auth_tuple = authenticator.authenticate(self)
File "/lib/python3.6/site-packages/rest_framework_jwt/authentication.py", line 33, in authenticate
payload = jwt_decode_handler(jwt_value)
File "/lib/python3.6/site-packages/rest_framework_jwt/utils.py", line 105, in jwt_decode_handler
secret_key = jwt_get_secret_key(unverified_payload)
File "/lib/python3.6/site-packages/rest_framework_jwt/utils.py", line 26, in jwt_get_secret_key
user = User.objects.get(pk=payload.get('user_id'))
File "/lib/python3.6/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/lib/python3.6/site-packages/django/db/models/query.py", line 431, in get
self.model._meta.object_name

From the JWT token, you are decoding it to get the user_id - payload['user_id'].
The error is happening because of User.objects.get(pk=payload.get('user_id')).
Instead of doing a get, you could use a get_object_or_404. Use it like so:
from django.shortcuts import get_object_or_404
payload = jwt_decode_handler(jwt_value)
user = get_object_or_404 (User, pk=payload.get('user_id'))
This raises a 404 error when a user will not be found; and that will be bubbled up through your view and handlers to return a 404 statuscode.

The suggestion by Druhn Bala works but would return a 404 error which isn't ideal for my use case. Instead I came up with one that returns a custom response. ValidationError
from rest_framework.exceptions allows you to send a 400 error with a custom response.
def jwt_decode_handler(token):
options = {
'verify_exp': api_settings.JWT_VERIFY_EXPIRATION,
}
# get user from token, BEFORE verification, to get user secret key
try:
unverified_user = jwt.decode(token, None, False)
except User.DoesNotExist:
raise ValidationError({"errors": ['Oops! Something went wrong, please logout and login back in!']})
secret_key = unverified_user.securitysettings.jwt_secret #my custom way of storing a unique jwt uuid per user.
return jwt.decode(
token,
api_settings.JWT_PUBLIC_KEY or secret_key,
api_settings.JWT_VERIFY,
options=options,
leeway=api_settings.JWT_LEEWAY,
audience=api_settings.JWT_AUDIENCE,
issuer=api_settings.JWT_ISSUER,
algorithms=[api_settings.JWT_ALGORITHM]
)
Lastly we set the custom decode handler as the default in settings.py.
JWT_AUTH = {
'JWT_DECODE_HANDLER':
'registration.decoder.jwt_decode_handler',
...
}

Related

AttributeError: 'Serializer' object has no attribute '_meta'

i have created this serializer to validate on the presence of a record with these column combination`
class CampaignServicesValidation(serializers.Serializer):
campaign_id = serializers.IntegerField(required=True)
service_id = serializers.IntegerField(required=True)
def validate(self, data):
try:
campaign_service = CampaignServices.objects.get(campaign_id=data['campaign_id'],
service_id=data['service_id'])
print("found"+str(campaign_service.id))
except Exception:
raise serializers.ValidationError(detail='Campaign Service does not exist')
return campaign_service
and it is called in my viewSet like this:
campaign_service = CampaignServicesValidation(data={'campaign_id': request.data['campaign_id'], 'service_id': pk})
if not campaign_service.is_valid():
return RestResponse(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, serializer_error=campaign_service.errors)
when the combination is not found it raises an exception and works well, but when it passes validation and enters the is_valid() function in the if condition it produces this error
Traceback (most recent call last):
File "D:\RightsHero\collector-management\venv\lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "D:\RightsHero\collector-management\venv\lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "D:\RightsHero\collector-management\venv\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "D:\RightsHero\collector-management\venv\lib\site-packages\rest_framework\viewsets.py", line 125, in view
return self.dispatch(request, *args, **kwargs)
File "D:\RightsHero\collector-management\venv\lib\site-packages\rest_framework\views.py", line 509, in dispatch
response = self.handle_exception(exc)
File "D:\RightsHero\collector-management\venv\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
self.raise_uncaught_exception(exc)
File "D:\RightsHero\collector-management\venv\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
raise exc
File "D:\RightsHero\collector-management\venv\lib\site-packages\rest_framework\views.py", line 506, in dispatch
response = handler(request, *args, **kwargs)
File "D:\RightsHero\collector-management\collectors_management\views.py", line 157, in update_campaign_service_frequency
serializer.save()
File "D:\RightsHero\collector-management\venv\lib\site-packages\rest_framework\serializers.py", line 207, in save
self.instance = self.update(self.instance, validated_data)
File "D:\RightsHero\collector-management\venv\lib\site-packages\rest_framework\serializers.py", line 993, in update
info = model_meta.get_field_info(instance)
File "D:\RightsHero\collector-management\venv\lib\site-packages\rest_framework\utils\model_meta.py", line 35, in get_field_info
opts = model._meta.concrete_model._meta
AttributeError: 'CampaignServicesValidation' object has no attribute '_meta'
I have tried changing serializer.Serializer to ModelSerialzier but nothing changed
Since Serializer does not have a _meta attribute, it does not have the is_valid() method. Instead, you should check the serializer.errors attribute of the serializer after calling validate().

django-rest-knox - how to authenticate token for email verification?

I am trying to set-up email verification using django-rest-knox. The link is being sent out. When I use the following:
from knox.auth import TokenAuthentication
class VerifyEmailAPI(generics.GenericAPIView):
def get(self, request):
# Get the token from request
# token = request.GET.get('token')
# print(token)
user = TokenAuthentication.authenticate(request, self)
print(user)
return Response({'message': 'Just testing for now'})
The url:
http://127.0.0.1:8000/api/auth/email-verify/?token=58cab01ad07801dbe5e6c4fc5c6c7ef060bf7912cb723dc70e4c5fb677fbbf1e
I get this error:
AttributeError: 'VerifyEmailAPI' object has no attribute 'META'
Complete traceback:
Traceback (most recent call last):
File "/Users/sid/eb-virt/lib/python3.8/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
File "/Users/sid/eb-virt/lib/python3.8/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/sid/eb-virt/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "/Users/sid/eb-virt/lib/python3.8/site-packages/django/views/generic/base.py", line 84, in view
return self.dispatch(request, *args, **kwargs)
File "/Users/sid/eb-virt/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch
response = self.handle_exception(exc)
File "/Users/sid/eb-virt/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception
self.raise_uncaught_exception(exc)
File "/Users/sid/eb-virt/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
raise exc
File "/Users/sid/eb-virt/lib/python3.8/site-packages/rest_framework/views.py", line 506, in dispatch
response = handler(request, *args, **kwargs)
File "/Volumes/coding/fort_dev/accounts/api.py", line 75, in get
user = TokenAuthentication.authenticate(request, self)
File "/Users/sid/eb-virt/lib/python3.8/site-packages/knox/auth.py", line 37, in authenticate
auth = get_authorization_header(request).split()
File "/Users/sid/eb-virt/lib/python3.8/site-packages/rest_framework/authentication.py", line 20, in get_authorization_header
auth = request.META.get('HTTP_AUTHORIZATION', b'')
AttributeError: 'VerifyEmailAPI' object has no attribute 'META'
Am I doing something fundamentally wrong when using the django-rest-knox function?

Unable to use custom account adapter

I have the following in adapter.py:
from allauth.account.adapter import DefaultAccountAdapter
class CustomAllauthAdapter(DefaultAccountAdapter):
pass # keeping it trivial for debugging
At the very bottom of settings.py:
import django
django.setup() # complains about apps not being loaded yet without this...
from .adapter import CustomAllauthAdapter
ACCOUNT_ADAPTER = CustomAllauthAdapter # this is the line that results in the error!
As soon as I submit the registration form for a new user, I get this error in the browser:
AssertionError at /api/v1/users/auth/register/
No exception message supplied
Here is the Traceback:
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
response = get_response(request)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\django\views\generic\base.py", line 70, in view
return self.dispatch(request, *args, **kwargs)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\django\utils\decorators.py", line 43, in _wrapper
return bound_method(*args, **kwargs)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\django\views\decorators\debug.py", line 89, in sensitive_post_parameters_wrapper
return view(request, *args, **kwargs)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\dj_rest_auth\registration\views.py", line 47, in dispatch
return super().dispatch(*args, **kwargs)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\rest_framework\views.py", line 509, in dispatch
response = self.handle_exception(exc)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
self.raise_uncaught_exception(exc)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
raise exc
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\rest_framework\views.py", line 506, in dispatch
response = handler(request, *args, **kwargs)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\rest_framework\generics.py", line 190, in post
return self.create(request, *args, **kwargs)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\dj_rest_auth\registration\views.py", line 66, in create
serializer.is_valid(raise_exception=True)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\rest_framework\serializers.py", line 220, in is_valid
self._validated_data = self.run_validation(self.initial_data)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\rest_framework\serializers.py", line 419, in run_validation
value = self.to_internal_value(data)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\rest_framework\serializers.py", line 478, in to_internal_value
validated_value = validate_method(validated_value)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\dj_rest_auth\registration\serializers.py", line 209, in validate_email
email = get_adapter().clean_email(email)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\allauth\account\adapter.py", line 535, in get_adapter
return import_attribute(app_settings.ADAPTER)(request)
File "C:\Dropbox\Parnasa\Web\drmeir\env\lib\site-packages\allauth\utils.py", line 153, in import_attribute
assert isinstance(path, str)
If I do not set the ACCOUNT_ADAPTER, everything works well including registration and authentication. I need to use custom account adapter to customize email verification.
Why am I getting the error and what should I do to fix it?
No need to import anything and no need for django.setup(). The configuration variable is a string!
ACCOUNT_ADAPTER = 'users.adapter.CustomAllauthAdapter'

Django JsonResponse: User matching query does not exist

So I´ve tried to make an ajax for my models but it is giving me a matching query does not exist, here is the full error:
Internal Server Error: /messages/notification/
Traceback (most recent call last):
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\views\generic\base.py", line 71, in view
return self.dispatch(request, *args, **kwargs)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\contrib\auth\mixins.py", line 52, in dispatch
return super().dispatch(request, *args, **kwargs)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\views\generic\base.py", line 97, in dispatch
return handler(request, *args, **kwargs)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\views\generic\detail.py", line 106, in get
self.object = self.get_object()
File "C:\Users\berna\Desktop\Python & Javascript\Web development\MiFamiliaEsUnDesastre\mifamiliaesundesastre\chat\views.py", line 26, in get_object
obj, created = Thread.objects.get_or_new(self.request.user, other_username)
File "C:\Users\berna\Desktop\Python & Javascript\Web development\MiFamiliaEsUnDesastre\mifamiliaesundesastre\chat\models.py", line 29, in get_or_new
user2 = Klass.objects.get(username=other_username)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\db\models\manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\db\models\query.py", line 415, in get
raise self.model.DoesNotExist(
django.contrib.auth.models.User.DoesNotExist: User matching query does not exist.
HTTP GET /messages/notification/ 500 [0.16, 127.0.0.1:56012]
Also here is my code:
models.py
class ProfileImage(models.Model):
"""
Profile model
"""
user = models.OneToOneField(
verbose_name=_('User'),
#to=settings.AUTH_USER_MODEL,
to = User,
related_name='profile',
on_delete=models.CASCADE
)
avatar = models.ImageField(upload_to='profile_image')
notifications = models.FloatField(default='0')
views.py
def notifications(request, user_id, *args, **kwargs):
user = User.objects.get(pk=user_id)
user.profile.notifications = user.profile.notifications + 1
user.save()
return JsonResponse(data=user.profile.notifications, safe=False)
urls.py
path('messages/notification/', notifications)
my html for the ajax call
// Add the notification val
$.get('notification/')
someone tell me that is that I dont have users, but when I do have a user, I dont know what is going on
You need to send user_id to the view:
path('messages/notification/(?P<user_id>[a-zA-Z0-9/_\.-]*)', notifications)
and:
$.get('messages/notification/{{ user.pk }}')
Update
You have to know their user id to get their notifications:
$.get('messages/notification/12234')
I assume you have a User model somewhere?
Also, user_id is coming in as a string, so need to int() that:
user = User.objects.get(pk=int(user_id))
Or use:
path('messages/notification/<int:user_id>', notifications)
So for does who saw this later, what I did is to change the
path('messages/notification/', notifications)
To
url(r'^ajax/notification/$', notification),

Django Rest Framework APIClient not handling exceptions during tests

I'm testing an API endpoint that is supposed to raise a ValidationError in a Django model (note that the exception is a Django exception, not DRF, because it's in the model).
from rest_framework.test import APITestCase
class TestMyView(APITestCase):
# ...
def test_bad_request(self):
# ...
response = self.client.post(url, data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
However, my test errors out with an exception instead of passing. It doesn't even fail getting a 500 instead of 400, it doesn't get there at all. Isn't DRF's APIClient supposed to handle every exception? I've search online but found nothing. I've read that DRF doesn't handle Django's native ValidationError, but still that doesn't explain why I am not even getting a 500. Any idea what I'm doing wrong?
Full stack trace:
E
======================================================================
ERROR: test_cannot_create_duplicate_email (organizations.api.tests.test_contacts.TestContactListCreateView)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/code/organizations/api/tests/test_contacts.py", line 98, in test_cannot_create_duplicate_email
response = self.jsonapi_post(self.url(new_partnership), data)
File "/code/config/tests/base.py", line 166, in jsonapi_post
url, data=json.dumps(data), content_type=content_type)
File "/usr/local/lib/python3.7/site-packages/rest_framework/test.py", line 300, in post
path, data=data, format=format, content_type=content_type, **extra)
File "/usr/local/lib/python3.7/site-packages/rest_framework/test.py", line 213, in post
return self.generic('POST', path, data, content_type, **extra)
File "/usr/local/lib/python3.7/site-packages/rest_framework/test.py", line 238, in generic
method, path, data, content_type, secure, **extra)
File "/usr/local/lib/python3.7/site-packages/django/test/client.py", line 422, in generic
return self.request(**r)
File "/usr/local/lib/python3.7/site-packages/rest_framework/test.py", line 289, in request
return super(APIClient, self).request(**kwargs)
File "/usr/local/lib/python3.7/site-packages/rest_framework/test.py", line 241, in request
request = super(APIRequestFactory, self).request(**kwargs)
File "/usr/local/lib/python3.7/site-packages/django/test/client.py", line 503, in request
raise exc_value
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python3.7/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/django/views/generic/base.py", line 71, in view
return self.dispatch(request, *args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/rest_framework/views.py", line 495, in dispatch
response = self.handle_exception(exc)
File "/usr/local/lib/python3.7/site-packages/rest_framework/views.py", line 455, in handle_exception
self.raise_uncaught_exception(exc)
File "/usr/local/lib/python3.7/site-packages/rest_framework/views.py", line 492, in dispatch
response = handler(request, *args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/rest_framework/generics.py", line 244, in post
return self.create(request, *args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/rest_framework/mixins.py", line 21, in create
self.perform_create(serializer)
File "/usr/local/lib/python3.7/site-packages/rest_framework/mixins.py", line 26, in perform_create
serializer.save()
File "/usr/local/lib/python3.7/site-packages/rest_framework/serializers.py", line 214, in save
self.instance = self.create(validated_data)
File "/code/organizations/api/serializers.py", line 441, in create
'partnership': self.context['partnership']
File "/usr/local/lib/python3.7/site-packages/rest_framework/serializers.py", line 943, in create
instance = ModelClass._default_manager.create(**validated_data)
File "/usr/local/lib/python3.7/site-packages/django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/django/db/models/query.py", line 422, in create
obj.save(force_insert=True, using=self.db)
File "/code/organizations/models.py", line 278, in save
self.full_clean()
File "/usr/local/lib/python3.7/site-packages/django/db/models/base.py", line 1203, in full_clean
raise ValidationError(errors)
django.core.exceptions.ValidationError: {'__all__': ['Supplier contact emails must be unique per organization.']}
Question: Isn't DRF's APIClient supposed to handle every exception?
Answer: No. It's a test client, it won't handle any uncaught exceptions, that's how test clients work. Test clients propagate the exception so that the test fails with a "crash" when an exception isn't caught. You can test that exceptions are raised and uncaught with self.assertRaises
Question: The APIView should return HTTP_400_BAD_REQUEST when I raise a ValidationError but the exception isn't caught.
Answer:
You should look at the source code for APIView.
Inside the dispatch() method, all exceptions raised while creating the response object are caught and the method handle_exception() is called.
Your exception is a ValidationError. The crucial lines are:
exception_handler = self.get_exception_handler()
context = self.get_exception_handler_context()
response = exception_handler(exc, context)
if response is None:
self.raise_uncaught_exception(exc)
If you haven't changed settings.EXCEPTION_HANDLER, you get the default DRF exception handler, source code here.
If handles Http404, PermissionDenied and APIException. The APIView itself actually also handles AuthenticationFailed and NotAuthenticated. But not ValidationError. So it returns None and therefore the view raises your ValidationError which stops your test.
You see that in your traceback:
File "/usr/local/lib/python3.7/site-packages/rest_framework/views.py", line 455, in handle_exception
self.raise_uncaught_exception(exc)
You can decide to handle more exceptions than the default ones handled by DRF, you can read this on custom exception handling.
EDIT: You can also raise rest_framework.exceptions.ValidationError instead of the standard Django ValidationError. That is an APIException and therefore will be handled by DRF as a HTTP400_BAD_REQUEST. [1]
Side note: Luckily DRF doesn't catch every single exception! If there's a serious flaw in your code you actually want your code to "crash" and produce an error log and your server to return a HTTP 500. Which is what happens here. The response would be an HTTP 500 if this wasn't the test client.
[1]https://github.com/encode/django-rest-framework/blob/3.9.0/rest_framework/exceptions.py#L142
Something in your code is causing a python error which is halting execution before your POST request can return a valid HTTP response. Your code doesn't even reach the line self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) because there is no response.
If you're calling your tests in the normal way with ./manage.py test then you should see the traceback and be able to narrow down what caused the error.
Django >3.0
Starting with Django 3.0, Client constructor takes raise_request_exception parameter. Set it False and response 500 will returned instead of raising an exception.
Source:
https://docs.djangoproject.com/en/3.2/releases/3.0/#tests
self.client = Client(raise_request_exception=False)
Django <3.0
There is a workaround for this for Django 2.2 and earlier.
TL;DR:
Just mock the store_exc_info method of Client class.
from unittest import mock
class TestMyView(APITestCase):
...
def test_server_error(self):
mock.patch("django.test.client.Client.store_exc_info")
self.client.post("/url/, data)
...
Now, the client will return regular response object with status code = 500.
Explanation:
Django's Test client always raises exceptions, even if they were wrapped in 500 Server error response.
Client connects its method called store_exc_info to the got_request_exception Django's signal (source: https://github.com/django/django/blob/3.2.5/django/test/client.py#L712)
got_request_exception.connect(self.store_exc_info, dispatch_uid=exception_uid)
This method saves an exception info as self.exc_info. This attribute is later tested if its None. If it is, then error is raised. If not, response is normally returned (source: https://github.com/django/django/blob/2.2.24/django/test/client.py#L500)
Mocking the self.store_exc_info makes it do nothing, so exception info is not stored - therefore it's None later :)