목표는 다음과 같은 필드를 가진 User 테이블을 생성하는 것이다.
python [manage.py](http://manage.py) startapp
을 통해 authentication app을 생성했다.
serializers의 역할은 직렬화와 역직렬화라고 한다.
나는 다음과 같은 의도로 사용했다.
{"user_id": "admin", "password", ...}
이런식으로 직렬화. 이 3가지는 다음에 views.py에서 설명함.
from .models import User
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
class Meta
: 이 클래스는 serializer가 작동하기에 필요한 동작을 명세하는 곳model = User
이 serializer가 User 모델을 따른다는 뜻fields = “__all__”
: User의 모든 필드가 직렬화에 필요함. 추가적으로 다른 모델과 중첩으로 이 serializer 를 구성하고 싶다면 아래와 같이 하면 된다.class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ['username', 'email', 'profile']
def create(self, validated_data):
profile_data = validated_data.pop('profile')
user = User.objects.create(**validated_data)
Profile.objects.create(user=user, **profile_data)
return user
model은 테이블에서 나타날 필드를 작성하는 곳.
작성하고
python [manage.py](http://manage.py) makemigrations
python [manage.py](http://manage.py) migrate
를 수행하면
mysqlworkbench에서 보았을 때 다음과 같이 테이블이 생성됨을 볼 수 있다.
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.contrib.auth.hashers import make_password
# 이 클래스는 User에 대해 생성하는 과정을 담당한다.
class UserManager(BaseUserManager):
def create_user(self, user_email, password, **extra_fields):
if password is None:
raise TypeError("User must have a password")
extra_fields.setdefault("is_admin", False)
extra_fields.setdefault("is_staff", False)
# User에서 정의한대로 인스턴스 생성
user = self.model(
user_email=user_email,
**extra_fields
)
user.set_password(password)
# .save 를 통해 db에 푸시.
user.save(using=self._db)
return user
# 이 함수는 특수한 방법 아니면 호출될 수 없다. python manage.py createsuperuser 같은 경우.
def create_superuser(self, user_email='admin.kookmin.ac.kr', password='', **extra_fields):
if password is None:
raise TypeError("SuperUser must have a password")
# admin 임을 지정
extra_fields.setdefault("is_admin",True)
extra_fields.setdefault("is_staff",True)
return self.create_user(user_email=user_email, password=password, **extra_fields)
# Create your models here.
class User(AbstractBaseUser):
# sqldbm (맨위 사진) 대로 필드 만들기
user_email = models.CharField(max_length=30)
user_nickname = models.CharField(max_length=10, unique=True)
user_definition = models.CharField(max_length=100, null=True)
user_profile_image = models.CharField(max_length=200, null=True)
user_point_number = models.IntegerField(default=0, null=True)
# 이 유저가 스텝인지 어드민인지 boolean으로 구분
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
#Usernamedmf user_nickname으로 취급한다. username과 password는 django에서 기본적으로 인증을 위해 필수적인 사항이므로 이렇게 지정해야한다.
USERNAME_FIELD = "user_nickname"
REQUIRED_FIELD = ['user_nickname']
# 이 객체의 Manager 지정.
objects = UserManager()
def save(self, *args, **kwargs):
if self.password:
self.password = make_password(self.password)
super().save(*args, **kwargs)
django에는 기본적으로 User 모델이 있지만 우리가 원하는 User 모델은 추가적인 필드가 필요하므로 AbstractBaseUser를 상속해서 재정의했다.
from django.urls import path, include
from . import views
from .views import UserSignupView, UserLoginView, UserInfoView
urlpatterns = [
path('signup/', UserSignupView.as_view(), name="user_signup_view"),
path('login/', UserLoginView.as_view(), name="user_login_view"),
path('info/', UserInfoView.as_view(), name="user_info_view")
]
database/urls와는 다른 authentication/urls 이다.
database/urls는 루트 경로를 정의하고 루트 경로에서 user/
는 이 authentication/urls 를 따른다고 작성했기 때문에 [http://127.0.0.1:8000/user/](http://127.0.0.1:8000/user/)
으로 시작하는 경로는 위와 같은 urlpatterns를 따른다.
.../signup/
: UserSignupView를 실행한다..../login/
:UserLoginView를 실행한다..../info/
:UserInfoView를 실행한다.http://127.0.0.1:8000/user/signup
이런식으로 인터넷 주소로 들어갔을 때 여기 views.py 의 클래스들을 이용하도록 urls.py에 작성했다.
from rest_framework.authtoken.models import Token
from rest_framework import permissions, status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.authentication import TokenAuthentication
from .serializers import UserSerializer
from .models import User
from django.contrib.auth import authenticate
#user/signup/
class UserSignupView(APIView):
# User.objects.all() : User 모델의 모든 객체를 조회하는 쿼리
queryset = User.objects.all()
serializer_class = UserSerializer
def post(self, request):
serializer = UserSerializer(data=request.data) # serializer에 data 적용
data = {} # 이 딕셔너리는 return Response(data)를 통해 요청한 유저에게 제공될 정보. Response 함수는 json 변환을 지원하나보다.
if serializer.is_valid(): # serializer가 validation 수행
user = serializer.save() # db에 저장
data['user_nickname'] = serializer.data['user_nickname']
else: # 오류
data = serializer.errors
return Response(data, status=status.HTTP_400_BAD_REQUEST)
return Response(data)
#user/login/
class UserLoginView(APIView):
queryset = User.objects.all()
serializer_class = UserSerializer
def post(self, request, *args, **kwargs):
user_nickname = request.data["user_nickname"]
password = request.data["password"]
# authenticate : username과 password를 통해 User 테이블의 정보와 맞는지 검색.
user = authenticate(user_nickname=user_nickname, password=password)
if user:
token, created = Token.objects.get_or_create(user=user) # .get_or_create : 유저에 해당하는 컬럼에 이미 토큰이 있으면 get 없으면 create 후 get
# 토큰은 별도의 테이블에 저장됨
return Response({"token": token.key})
else:
return Response(status=status.HTTP_400_BAD_REQUEST)
#user/info/
class UserInfoView(APIView):
# Token 으로 유저의 정보를 탐색
def post(self, request):
token = request.data["token"]
user_id = Token.objects.filter(key=token).values().first()["user_id"]
user_info = User.objects.filter(id=user_id).values().first()
return Response(user_info)
python [manage.py](http://manage.py) runserver
이후
[http://127.0.0.1:8000/user](http://127.0.0.1:8000/user/)/signup
접속
(순차적으로 id,password, last_login, user_email, user_nickname, user_definition, user_profile_image, user_point_number, is_admin, is_staff)
[http://127.0.0.1:8000/user](http://127.0.0.1:8000/user/)/login
접속
[http://127.0.0.1:8000/user](http://127.0.0.1:8000/user/)/info
접속