概要
カスタムユーザで
- 社員番号
- パスワード
を使ってログイン/ログアウトを行います
実際の挙動はSwaggerを使って確認します
必要な設定ファイルを記述
- models.py
- settings.py
- serilaizers.py
- views.py
- urls.py
に必要な情報を記載します
- models.py
- settings.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"]
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()
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を使ってください
@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にします
@action(detail=False, methods=["POST"])
requestのdata内の情報は以下の通りです
print(request.data) # {'employee_number': '00000001', 'password': 'test'}
LoginSerializerにrequest.dataが入り、serilaizerの変数に代入されます
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のレスポンスが返ってきます
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_number
とpassword
の変数に代入していきます
Djangoにはauthenticate
というメソッドでユーザの認証を行うことができます
データベース内にuserが存在しない場合は400とエラーメッセージを返します
userが存在する場合はDjangoのlogin
メソッドが実行され、login
が成功します
今回はJsonResponseとしてroleが返ってくるよう設定します
logout
@action(methods=["POST"], detail=False)
def logout(self, request):
logout(request)
return HttpResponse()
logout
はlogin
と比べると簡単でDjangoのlogout
メソッドを使って実装します
まとめ
Djangoの
- is_valid
- authenticate
- login
- logout
メソッドを使うと楽に実装できました
しかし、現状の実装だけではログインしてもしなくてもAPIを使用できてしまっているので
下記のようにPermissionを実装するとログインしたユーザ以外はAPIを使うことができなくなります
興味がある方は見ていただけると幸いです
記事の紹介
以下の記事も書いたのでよかったら読んでみてください
参考