1) Create a new app user
python manage.py startapp user2) Remove the following redandunt files
# all database stufffs will be in core app a) migrations folder b) models.py c) admin.py # remove test.py and create a directory test with __init__.py3) Add rest_framework and user to INSTALLED_APPS in settings.py
For now, let us target for an API that will manage creation of users and write testcases for each senario
We ll use rest_framework.test.APIClient to simulate the API client
Remember, users created in each test will not be saved to the actual db. So for each test we have to create new users.
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.urls import reverse
from rest_framework.test import APIClient
from rest_framework import status
CREATE_USER_URL = reverse("user:create")
def create_user(**kwargs):
return get_user_model().objects.create_user(**kwargs)
# 'Public' because we dont check for authentication
class PublicUserApiTests(TestCase):
"""Test the users API (public)"""
def setUp(self):
self.client = APIClient()
# test API call to create new user
def test_create_valid_user_success(self):
"""test creating user with valid payload
payload is a POST dict"""
payload = {
'email': 'test@test.com',
'password': 'testpass',
'name': 'tester'
}
# This call to API should create a new user
res = self.client.post(CREATE_USER_URL, payload)
# assert if the response is 201 created
self.assertEqual(res.status_code, status.HTTP_201_CREATED)
# get this user and check if password is correct
user = get_user_model().objects.get(**res.data)
self.assertTrue(user.check_password(payload['password']))
# assert that the password is not returned in the response data
self.assertNotIn('password', res.data)
# Test api when trying to create existing users
def test_user_exists(self):
# Note, the users created in previous test cases do not exist
payload = {'email': 'test@test.com', 'password': 'testpass'}
# create this user
create_user(**payload)
# try to create this user again with APIClient
res = self.client.post(CREATE_USER_URL, payload)
# assert that the response is 400
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
# Test if user is not created when password is too short
def test_password_too_short(self):
# Note, the users created in previous test cases do not exist
payload = {'email': 'test@test.com', 'password': 'tp'}
res = self.client.post(CREATE_USER_URL, payload)
# assert response failed
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
# assert user was not created
user_exists = get_user_model().objects.filter(
email=payload['email']
).exists()
self.assertFalse(user_exists)
1) For an API to create user, we ll have to define a serializer for user model
2) Create a new file named serializers.py under user app
from django.contrib.auth import get_user_model
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
"""serializer for user object"""
class Meta:
model = get_user_model()
fields = ('email', 'password', 'name')
# the password should be write only.
# it should not be serialized when get is called
# we specify extra kwargs for each field
# list of accepted args for can be found under core argument section of
# https://www.django-rest-framework.org/api-guide/fields/
# for password field, args under serializer.CharField are also valid
extra_kwargs = {'password': {'write_only': True, 'min_length': 5}}
# create is called when we use the CreateAPI view
# which takes a POST request to create a user
def create(self, validated_data):
return get_user_model().objects.create_user(**validated_data)
3) define a view to create users in views.py
from rest_framework import generics
from user.serializers import UserSerializer
# Create your views here.
class CreateUserView(generics.CreateAPIView):
"""Create a new user"""
serializer_class = UserSerializer
4) create urls
#user/urls.py
from django.urls import path
from user import views
app_name = 'user'
urlpatterns = [
path('create/', views.CreateUserView.as_view(), name='create'),
]
#app/urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('api/user/', include('user.urls')),
]
5) On typing the url on the browser, (but this link should be called from client code with POST dict)