Search code examples
pythondjangodjango-rest-frameworkpython-3.9django-rest-framework-simplejwt

Overriding DRF settings for tests


I'm using Python 3.9, Django 3.2, DRF 3.12.4. I'm adding JWT authentication method using simple JWT. To test my auth method, I need to set

"DEFAULT_AUTHENTICATION_CLASSES": (
    "rest_framework_simplejwt.authentication.JWTAuthentication",
),

I would like to avoid editing my settings file and keep all changes in my test file. That's because I have separate local and production settings, and local settings are using fake auth backend. All tests are passing when I set the above auth class in my local settings.

I tried to use @override_settings:

@override_settings(
    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES": (
            "rest_framework_simplejwt.authentication.JWTAuthentication",
        ),
    }
)

It seems to work fine - when I print the settings in my test:

from django.conf import settings
from rest_framework.settings import api_settings

...

    def setUp(self):
        print(settings.REST_FRAMEWORK)
        print(api_settings.DEFAULT_AUTHENTICATION_CLASSES)

I get

{'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework_simplejwt.authentication.JWTAuthentication',)}
[<class 'rest_framework_simplejwt.authentication.JWTAuthentication'>]

However, tests aren't passing - I'm getting error from my fake auth backend, as if the settings aren't getting overriden after all. I also tried editing django.conf settings directly - which is something I would rather not do, but it didn't solve my problem as well. Am I doing something wrong? Maybe my solution/tests have some logical mistakes and I'm doing something I shouldn't do? Thank you in advance for all the help.


Solution

  • This happens because several DRF base classes store the initial values on the class.

    You can update the various cached values like this:

    from rest_framework.generics import GenericAPIView
    from rest_framework.settings import api_settings
    from rest_framework.test import APIRequestFactory
    from rest_framework.views import APIView
    
    GenericAPIView.filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
    GenericAPIView.pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
    
    APIView.renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
    APIView.parser_classes = api_settings.DEFAULT_PARSER_CLASSES
    APIView.authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    APIView.throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
    APIView.permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
    APIView.content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
    APIView.metadata_class = api_settings.DEFAULT_METADATA_CLASS
    APIView.versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
    
    APIRequestFactory.renderer_classes_list = api_settings.TEST_REQUEST_RENDERER_CLASSES
    APIRequestFactory.default_format = api_settings.TEST_REQUEST_DEFAULT_FORMAT
    

    If you want to be able to have this happen automatically when using override_settings, add a signal listener for setting_changed.

    from django.test.signals import setting_changed
    
    from rest_framework.generics import GenericAPIView
    from rest_framework.settings import api_settings
    from rest_framework.test import APIRequestFactory
    from rest_framework.views import APIView
    
    
    def reload_drf_settings(*args, **kwargs):
        setting = kwargs['setting']
        if setting == 'REST_FRAMEWORK':
            api_settings.reload()
            
            GenericAPIView.filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
            GenericAPIView.pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
    
            APIView.renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
            APIView.parser_classes = api_settings.DEFAULT_PARSER_CLASSES
            APIView.authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
            APIView.throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
            APIView.permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
            APIView.content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
            APIView.metadata_class = api_settings.DEFAULT_METADATA_CLASS
            APIView.versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
    
            APIRequestFactory.renderer_classes_list = api_settings.TEST_REQUEST_RENDERER_CLASSES
            APIRequestFactory.default_format = api_settings.TEST_REQUEST_DEFAULT_FORMAT
    
    
    setting_changed.connect(reload_drf_settings)