LoginSignup
1

Django Rest Frameworkでログイン/ログアウト機能を実装してみよう!

Last updated at Posted at 2022-11-06

概要

カスタムユーザで

  • 社員番号
  • パスワード

を使ってログイン/ログアウトを行います
実際の挙動はSwaggerを使って確認します

必要な設定ファイルを記述

  • models.py
  • settings.py
  • serilaizers.py
  • views.py
  • urls.py

に必要な情報を記載します

  • models.py
  • settings.py

は下記の記事を参考に作成してください

serializers.py
from rest_framework import serializers
from .models import User


class UserSerilaizer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ["id","employee_number","username", "email", "role"]
        read_only_fields = ["id", "created_at","updated_at"]


class LoginSerializer(serializers.ModelSerializer):
    employee_number = serializers.CharField(max_length=255)

    class Meta:
        model = User
        fields = ["employee_number","password"]
views.py
from django.contrib.auth import authenticate, login, logout
from django.http import HttpResponse, JsonResponse
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.viewsets import ModelViewSet
from rest_framework.viewsets import ViewSet

from .models import User
from .serializers import LoginSerializer, UserSerilaizer


class UserViewSet(ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerilaizer


class LoginViewSet(ViewSet):
    serializer_class = LoginSerializer
    permission_classes = [AllowAny]

    @action(detail=False, methods=["POST"])
    def login(self, request):
        serializer = LoginSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        employee_number = serializer.validated_data.get("employee_number")
        password = serializer.validated_data.get("password")
        user = authenticate(employee_number=employee_number, password=password)
        if not user:
            return JsonResponse(
                data={"msg": "either employee number or password is incorrect"},
                status=status.HTTP_400_BAD_REQUEST,
            )
        else:
            login(request, user)
            return JsonResponse(data={"role": user.Role(user.role).name})

    @action(methods=["POST"], detail=False)
    def logout(self, request):
        logout(request)
        return HttpResponse()
アプリケーション/urls.py
from django.urls import path, include
from rest_framework_nested import routers

from application.views import (
    UserViewSet,
)

router = routers.DefaultRouter()
router.register(r'users', UserViewSet, basename='user')

urlpatterns = [
    path(r'', include(router.urls)),
]

プロジェクトのurlとSwaggerの設定をしたい場合は下記の記事を参考にしてください

では、一つずつ解説していきます

serilaizers.py

今回は

  • 社員番号
  • パスワード

でログインするため、LoginSerilaizerを新規で作成し、

  • employee_number
  • password

のみを対象にします

どうしてemployee_numberをオーバーライドするの?

employee_numberはuniqueな値です
loginする際はPOSTリクエストを送るのでデータベースにすでにそのユーザが存在するエラーが発生するのを防ぐために行います
仮にオーバーライドしないと以下のようなエラーが表示されます

{
  "employee_number": [
    "この 社員番号 を持った user が既に存在します。"
  ]
}

views.py

login

今回は

  • 社員番号:00000001
  • パスワード:test

のユーザでログインを行います

ご自身でもユーザを入れて検証してみたい方はcreatesuperuserでデータを入れるかfixtureを使ってください

views.py
    @action(detail=False, methods=["POST"])
    def login(self, request):
        serializer = LoginSerializer(data=request.data)
        if not serializer.is_valid():
            return JsonResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        employee_number = serializer.validated_data.get("employee_number")
        password = serializer.validated_data.get("password")
        user = authenticate(employee_number=employee_number, password=password)
        if not user:
            return JsonResponse(data={"msg": "either employee number or password is incorrect"}, status=status.HTTP_400_BAD_REQUEST)
        else:
            login(request, user)
            return JsonResponse(data={"role": user.Role(user.role).name})

今回はPOSTのみを実装したいので@actionデコレータを使用します
ログインする際はrequest内の情報だけで十分なのでdetail=Falseにします

views.py
@action(detail=False, methods=["POST"])

requestのdata内の情報は以下の通りです

print(request.data) # {'employee_number': '00000001', 'password': 'test'}

LoginSerializerにrequest.dataが入り、serilaizerの変数に代入されます

views.py
        serializer = LoginSerializer(data=request.data)
        if not serializer.is_valid():
            return JsonResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializer.is_valid()

でバリデーションを行います。これを行わないと以下のエラーが表示されます

When a serializer is passed a `data` keyword argument you must call `.is_valid()` before attempting to access the serialized `.data` representation.
You should either call `.is_valid()` first, or access `.initial_data` instead.

今回はModelで社員番号は8桁でバリデーションをかけているので例えば
8桁を超える社員番号を入れたとするとバリデーションエラーが発生し、400のレスポンスが返ってきます

スクリーンショット 2022-11-06 14.28.47.png

スクリーンショット 2022-11-06 14.31.10.png

views.py
        employee_number = serializer.validated_data.get("employee_number")
        password = serializer.validated_data.get("password")
        user = authenticate(employee_number=employee_number, password=password)
        if not user:
            return JsonResponse(data={"msg": "either employee number or password is incorrect"}, status=status.HTTP_400_BAD_REQUEST)
        else:
            login(request, user)
            return HttpResponse()

serializer.dataの中身は以下の通りになっています

print(serializer.data) # {'employee_number': '00000001', 'password': 'test'}

employee_numberpasswordの変数に代入していきます
Djangoにはauthenticateというメソッドでユーザの認証を行うことができます
データベース内にuserが存在しない場合は400とエラーメッセージを返します
スクリーンショット 2022-11-06 14.48.35.png

userが存在する場合はDjangoのloginメソッドが実行され、loginが成功します

スクリーンショット 2022-11-06 14.56.45.png

今回はJsonResponseとしてroleが返ってくるよう設定します

logout

views.py
    @action(methods=["POST"], detail=False)
    def logout(self, request):
        logout(request)
        return HttpResponse()

logoutloginと比べると簡単でDjangoのlogoutメソッドを使って実装します
スクリーンショット 2022-11-06 14.59.07.png

まとめ

Djangoの

  • is_valid
  • authenticate
  • login
  • logout

メソッドを使うと楽に実装できました
しかし、現状の実装だけではログインしてもしなくてもAPIを使用できてしまっているので
下記のようにPermissionを実装するとログインしたユーザ以外はAPIを使うことができなくなります
興味がある方は見ていただけると幸いです

記事の紹介

以下の記事も書いたのでよかったら読んでみてください

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1