27
loading...
This website collects cookies to deliver better user experience
serializer.py
file in the desired directory followed by the corresponding code responsible for doing the serialization.views.py
file responsible for building logic and presenting data to the client in different forms. DRF builds on top that idea and provide its own views for presenting data. They are two ways to represent views in DRF: function-based views and class-based views.APIView
.APIView
?APIException
exceptions and returns appropriate responses.NOTE
Some developers prefer the naming the views file api.py
. This is to keep separated from Django's views.py
file.
Response
object that extends Django's HttpRequest
. The extension allows it to provide a robust way of parsing requests. The Response
object provide a request.data
attribute that's similar to Django view's request.POST
but its more suitable for building web APIs.router
module for API URL routing to Django. It provides a consistent way of writing view logic to a set of URLs. To create routes for URLs, you create a router.py
file in the desired directory and then include
it in the Django URLconf (URL configuration).Authorization
header for every request. The server receives the token and checks it with what it has stored. If the tokens match, then the client is verified and given access.pip install django djang-restframework
in the terminal to install Django and DRF.python django-admin startproject [project name]
to start a new project. I'll be calling mine authentication.# Application definition
INSTALLED_APPS = [
...
"rest_framework",
"rest_framework.authtoken"
...
]
TokenAuthentication
and permission to AllowAny
. The AllowAny
is a general setting that applies to all views
of application, it allows the client to have access to all available views of the web app. However, this can be further streamlined to restrict certain views to authenticated users only, which you'll see as you move on.REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.TokenAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.AllowAny",
]
}
python manage.py migrate
to apply such changes.manage.py
command utility can be used to create a super user account.python manage.py createsuperuser --username johndoe --email [email protected]
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
class HelloView(APIView):
permissions_classes = [IsAuthenticated] # <-------- Only authenticated users can access this view
def get(self, request):
context = {"message": "Hello, World!"} # <------ Response to the client
return Response(context)
# urls.py
from django.urlsconf import path
from authentication.views import HelloView
urls_patterns = [
path("/api-token-auth", HelloView.as_view())
]
..authentication> http http://localhost:8000/api-is-authenticated
HTTP/1.1 401 Unauthorized
Allow: GET, HEAD, OPTIONS
...
Content-Type: application/json
...
WWW-Authenticate: Token # <----------- Take note of this
...
{
"detail": "Authentication credentials were not provided."
}
www-Authentication: Token
payload. That is to tell the client that a token is needed to access data on the http://localhost:8000/api-token-auth
URL. Later on, we'll test the view again, but with a valid token.serializers.py
file and write the following code:# serializers.py
from django.contrib.auth.models import User
from rest_framework import serializers
class RegistrationSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
"id",
"username",
"first_name",
"last_name",
"email",
"password",
"is_active",
"is_staff"
]
extra_kwargs = {"id": {"read_only": True}, "password": {"write_only": True}}
read_only_fields
def create(self, validated_data):
username = validated_data["username"]
first_name = validated_data["first_name"]
last_name = validated_data["last_name"]
email = validated_data["email"]
password = validated_data["password"]
user = User.objects.create_user(
username=username,
first_name=first_name,
last_name=last_name,
email=email,
password=password,
)
return user
RegistrationSerializer
class for validating and creating the user. The serializer will return an object that tells if the data is valid or not. The view will check if data is valid. If valid, it'll save it, generate token, and return response. If not valid, it'll return error response.# views.py
from django.contrib.auth.models import User
from authentication.serializers import RegistrationSerializer
from rest_framework import serializers
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.authtoken.models import Token
from rest_framework import permissions
class RegistrationView(APIView):
"""Registeration View"""
def post(self, request, *args, **kwargs):
"""Handles post request logic"""
registration_serializer = RegistrationSerializer(data=request.data)
# Generate tokens for existing users
for user in User.objects.all():
if not user:
break
else:
try:
Token.objects.get(user_id=user.id)
except Token.DoesNotExist:
Token.objects.create(user=user)
if registration_class.is_valid():
user = registration_serializer.save()
token = Token.objects.create(user=user)
return Response(
{
"user": {
"id": serializer.data["id"],
"first_name": serializer.data["first_name"],
"last_name": serializer.data["last_name"],
"username": serializer.data["username"],
"email": serializer.data["email"],
"is_active": serializer.data["is_active"],
"is_staff": serializer.data["is_staff"],
},
"status": {
"message": "User created",
"code": f"{status.HTTP_200_OK} OK",
},
"token": token.key,
}
)
return Response(
{
"error": serializer.errors,
"status": f"{status.HTTP_203_NON_AUTHORITATIVE_INFORMATION} \
NON AUTHORITATIVE INFORMATION",
}
)
# urls.py from django.urlsconf import path from authentication.views import RegistrationViewurls_patterns = [ #... path("/api-register-auth", RegistrationView.as_view())]
RegistrationView
. It should return the appropriate response with a token if required and valid credentials are provided.http --json POST http://localhost:8000/api/auth/register username="JamesChe" email="[email protected]" first_name="james" last_name="uche" password="jamesuche123456"{ "status": { "code": "200 OK", "message": "User created" }, "token": "b4784f96c3c65387bc8ea6463d5d4658cb32b0ac", "user": { "id": 2, "first_name": "james", "last_name": "uche", "username": "JamesChe", "email": "[email protected]", "is_active": true, "is_staff" true, }}
obtain_auth_token
view for creating and retrieving tokens for authenticated user.from rest_framework.authtoken.views import obtain_auth_token
then add the view to the URLconf.# urls.pyfrom rest_framework.authtoken.views import obtain_auth_tokenurl_patterns + [ path("/api-token-auth", obtain_auth_token.as_views())]
NOTE:
The naming of the URL pattern can be whatever you want.
..authentication> http post http//localhost:800/api-token-auth/ username="johndoe" password=123456HTTP/1.1 200 OKAllow: OPTION, POSTSContent-Type: application/json......{ "token": "bad492c010451bcba6acf7437706b8dd30eb11d5"}
HelloView
view? Let's test it by sending a GET request to its URL, this time with the token we just got...authentication> http http://localhost:8000/api-hello "Authorization: Token bad492c010451bcba6acf7437706b8dd30eb11d5"HTTP/1.1 200 OKAllow: GET, HEAD, OPTIONS.........{ "message": "Hello, World!" # <------------ Message because user token is valid.}
# Serializers.pyfrom django.contrib.auth.models import Userfrom rest_framework import serializersclass UserLoginSerializer(serializers.Serializer): """Login serializer""" username = serializers.CharField(required=True) password = serializers.CharField(required=True, read_only=True)class UserLoginResponse(serializers.ModelSerializer): """Response serializer""" class Meta: model = User fields = "id, username, first_name, last_name, email, password, is_active, is_staff" read_only_fields = ["id", "password", "is_active", "is_staff"]
# views.pyfrom django.contrib.auth.models import Userfrom authentication.serializers import LoginSerializerfrom authentication.serializers import LoginResponseSerializerfrom rest_framework import serializersfrom rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework import statusfrom rest_framework.authtoken.models import Tokenfrom rest_framework import permissionsfrom django.contrib.auth import authenticateclass LoginView(APIView): """Login View""" permissions_classes = [permissions.isAuthenticated] def post(self, request, *args, **kwargs): login_serializer = LoginSerializer(data=request.data) if login_serializer.is_valid(): user = authenticate(request, **serializer.data) if user is not None: response_class = LoginResponseSerializer(user) token, created_token = Token.objects.get_or_create(user_id=user.id) if isinstance(created_token, Token): token = created_token.key return Response( { "user": response_serializer.data, "status": { "message": "User Authenticated", "code": f"{status.HTTP_200_OK} OK", }, "token": token.key, } ) else: raise serializers.ValidationError( { "error": { "message": "Invalid Username or Password. Please try again", "status": f"{status.HTTP_400_BAD_REQUEST} BAD REQUEST", } } ) return( { "error": serializer.errors, "status": f"{status.HTTP_403_FORBIDDEN} FORBIDDEN" } )
# urls.pyfrom django.urls import pathfrom authentication.views import LoginViewurlpatterns = [ #... #... path("/api-login-auth", views.LoginView),]
..authentication> http --json post http//localhost:800/api-login-auth username="johndoe" password=123456HTTP/1.1 200 OKAllow: OPTION, POSTS...Content-Type: application/json......{ "status": { "code": "200 OK", "message": "User Authenticated" }, "token": "bad492c010451bcba6acf7437706b8dd30eb11d5", "user": { "email": "[email protected]", "first_name": "", "id": 1, "is_active": true, "is_staff": true, "last_name": "", "username": "johndoe" }}
obtain_auth_token
view for that.