How can I implement a Login system without using django default authentication in Python/Django?

Viewed 97

Suppose I have a self-made User Model which holds username and password field. Now I want to login with my self-made username and password in Django. How can I do that? Besides I need to build a Login Api using this username and password. How to do that?

1 Answers

I can implement the simple login system like the following.

TokenAuthentication can be used in the DRF by adding some configs in settings.py file.

# REST framework
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),

    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication'
    ]
}

Now create the users app by executing the following command.

python manage.py startapp users

And I can create the custom user model in the models.py. Let's say that the app name is "users" and the model name is "Account". You need to set the AUTH_USER_MODEL in settings.py.

# Application definition
INSTALLED_APPS = [
    ...
    'rest_framework',
    'corsheaders',
    'users',
    'rest_framework.authtoken'
]

# specify user model
AUTH_USER_MODEL = 'users.Account'

In models.py of the users app, I can define the user model by deriving from the AbstractBaseUser.

from django.db import models
from django.contrib.auth.models import AbstractBaseUser, UserManager
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token


class Account(AbstractBaseUser):
    """
    A model for users

    It simply has the three fields `username`, `password`, `last_login`.
    In addition, it has several shifts.
    """
    username = models.CharField(max_length=50, unique=True)

    USERNAME_FIELD = 'username'
    objects = UserManager()


@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
    if created:
        Token.objects.create(user=instance)

In serializers.py, you can set the password field as write-only.

from attr import fields
from rest_framework import serializers    
from .models import Account
    
class AccountSerializer(serializers.ModelSerializer):   
    class Meta:
        fields = '__all__'
        model = Account
        extra_kwargs = {
            'password': {'write_only': True}
        }

Finally, in urls.py of the users app, login can be implemented using rest_framework.authtoken

from django.urls import path
from rest_framework.authtoken import views
from .views import AccountView

urlpatterns = [
    path('login', views.obtain_auth_token, name="login"),
    path('register', AccountView.as_view(), name="register")
]

Of course, you can also implement the user register in views.py.

from django.shortcuts import render
from rest_framework.generics import CreateAPIView

from users.serializers import AccountSerializer
from .models import Account
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework import status

class AccountView(CreateAPIView):
    queryset = Account.objects.all()
    serializer_class = AccountSerializer
    permission_classes = [AllowAny]

    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        if serializer.is_valid():
            input_data = serializer.validated_data
            username = input_data.get('username')
            password = input_data.get('password')

            # create user and set password
            user = Account.objects.create(username=username)
            user.set_password(password)
            user.save()
            return Response(AccountSerializer(user).data, status=status.HTTP_201_CREATED)

        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Related