After recently discovering (and loving!) test driven development, I've started working on an API for a project of mine using Django with Django REST Framework and Django REST Framework API Key. However, I'm getting stuck trying to unit test some basic API functionality: every time I attempt to do a GET request to the API, it returns a 403: "Authentication credentials were not provided."
Which is odd; I am adding the credentials in the header of the request before I send it off. When I print out the header of the RequestsClient, I am getting this result:
{'User-Agent': 'python-requests/2.25.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'X-Api-Key': 'ADtIa4s9.joEWAwZH47uowolz60c1hYnskVujO8lQ'}
Should work... yet for some reason my server (Django's internal server, running on localhost:8000) will not see it. I've been going through a ton of Questions on here and I am left completely baffled. Can you help me out a bit? Thanks in advance.
settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework_api_key',
'core',
'base',
'api',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework_api_key.permissions.HasAPIKey',
]
}
API_KEY_CUSTOM_HEADER = "HTTP_X_API_KEY"
urls.py
from django.contrib import admin
from django.urls import path
from rest_framework import routers
from api.viewsets import RestaurantViewSet
router = routers.SimpleRouter()
router.register(r'restaurant', RestaurantViewSet)
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns += router.urls
api/viewsets.py
from rest_framework import viewsets
from .serializers import RestaurantSerializer
from base.models import Restaurant
class RestaurantViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Restaurant.objects.all()
serializer_class = RestaurantSerializer
base/tests.py
class RestaurantTest(APITestCase):
@classmethod
def setUpTestData(cls):
manager = User.objects.create(username="TestUser")
cls.restaurant = Restaurant.objects.create(name="Testaurant", manager=manager)
client_key = cls.restaurant.create_api_key()
...
cls.client = RequestsClient()
header = {'X-Api-Key': 'Api-Key {}'.format(client_key)}
cls.client.headers.update(header)
print(header) # Output: see description above
# This test passes
def test_resolves_to_correct_view(self):
found = resolve('/restaurant/')
self.assertEqual(found.func.__name__, RestaurantViewSet.__name__)
# This test fails with "Authentication credentials were not provided."
def test_able_to_get(self):
path = '/restaurant/'
response = self.client.get(path)
self.assertEqual(response.status_code, 200, msg="Request to '{}' failed: {} - {}".format(path, response.status_code, response.data['detail']))