CBV(Class Base View)
를 사용해 하나의 클래스 안에서 모든 api를 다룰 수 있게 된다.GET
: 조회POST
: 생성PUT
: 수정DELETE
: 삭제단독의 함수들을 통해 구현
된 Viewdef edit(request, id):
article = PostModel.objects.get(id=id)
movie = Movie.objects.get(id=article.title_id)
context = {
'article': article,
'movie': movie,
}
return render(request, 'post/edit.html', context)
클래스 내의 변수와 메소드를 조작
하여 보다 쉽게 구현할 수 있는 Viewclass UserView(APIView):
permission_classes = [permissions.AllowAny] # 누구나 view 조회 가능
def get(self, request):
return Response({'message': 'get method!!'})
def post(self, request):
return Response({'message': 'post method!!'})
def put(self, request):
return Response({'message': 'put method!!'})
def delete(self, request):
return Response({'message': 'delete method!!'})
$ python -m venv venv
$ source venv/bin/activate
$ pip install django
$ pip install djangoframework
$ pip freeze > requirement.txt
requirement.txt
를 만들어 깃에 올리게 되면, 다른 팀원이 모듈을 찾아서 설치할 필요없이 아래의 명령어를 통해 한 번에 다운받을 수 있다.$ pip install -r requirements.txt
$ django-admin startproject django_rest_framework .
$ django-admin startapp user
django_rest_framework/settings.py
에 입력한다. #### 상단 생략 ####
INSTALLED_APPS = [
'user',
'rest_framework',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
#### 하단 생략 ####
django_rest_framework/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('user.urls')),
]
as_view()
를 붙여줘야 한다.from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('user/', views.UserView.as_view()),
]
django의 rest framework에서 지원하는 permissions
를 사용하기 위해서는 APIView
를 import하고, permissions
또한 import 해야 한다.
permissions
의 종류
AllowAny
: 인증여부에 상관없이 view 호출 허용 (default)IsAuthenticated
: 인증된 요청에 한해서 view 호출 허용IsAdminUser
: Staff 인증 요청에 한해서 view 호출 허용IsAuthenticatedOrReadOnly
: 비인증 요청에게는 읽기 권한만 허용DjangoModelPermissions
: 인증된 요청에 한해서만 view 호출 허용, 추가로 유저별 인증 권한체크를 수행DjangoModelPermissionsOrAnonReadOnly
: DjangoModelPermissions 와 유사하나 비인증 요청에 대해서는 읽기 권한만 허용DjangoObjectPermissions
:비인증된 요청 거부. 인증된 레코드 접근에 대한 권한체크를 추가로 수행우리가 사용할 세 가지의 permissions에 대해 확인해보자.(command + 클릭
)
AllowAny
의 경우, 알다시피 모든 값이 True로 반환된다. 따라서 모두가 view에 접근할 수 있는 것이다. IsAuthenticated
의 경우, 불리언 값으로 사용자가 로그인을 해야 하고 인증이 되어 있는 경우에만 view에 접근할 수 있는지 반환하는 것을 볼 수 있다. IsAdminUser
의 경우, 위와 동일하게 불리언 값으로 사용자가 로그인을 해야 하고 관리자이어야만 view에 접근할 수 있다는 것을 알 수 있다.그리고 class에서 APIView를 상속받아 사용한다.
user/views.py
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import permissions
class UserView(APIView):
permission_classes = [permissions.AllowAny] # 누구나 view 접근 가능
# permission_classes = [permissions.IsAuthenticated] # 로그인된 사용자만 view 접근 가능
# permission_classes = [permissions.IsAdminUser] # admin 유저만 view 접근 가능
def get(self, request):
return Response({"msg": "get method!!" })
def post(self, request):
return Response({"msg": "post method!!" })
def put(self, request):
return Response({"msg": "put method!!" })
def delete(self, request):
return Response({"msg": "delete method!!" })
함수명
은 반드시 그대로 써줘야 한다. 이는 django에서 선언한 것들이기 때문에 만약 get을 gets로 바꿔서 신호를 보낸다면 아무런 결과값이 돌아오지 않는다.user/models.py
BaseUserManager
와 AbstractBaseUser
를 import 해야 한다. AbstractUser
가 아닌 AbstractBaseUser
를 상속해 사용하면 된다. from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
# custom user model
class User(AbstractBaseUser):
# "사용자 계정"은 admin 페이지에서 나오는 메뉴의 이름 / unique=True를 설정해 단 하나의 값만 입력할 수 있도록 함
username = models.CharField("사용자 계정", max_length=50, unique=True)
password = models.CharField("비밀번호", max_length=128)
email = models.EmailField("이메일", max_length=100)
name = models.CharField("이름", max_length=20)
# auto_now_add : 최초 생성 시의 시간을 자동으로 입력해줌.(그 후 업데이트에 대한 시간 기록은 x) 주로 가입일, 최초 생성일 등에 사용함
join_data = models.DateTimeField("가입일자", auto_now_add=True)
django_rest_framework/settings.py
AUTH_USER_MODEL
을 선언해 주지 않는다면, django는 자동으로 AUTH_USER_MODEL = "auth.User"
라는 기본 유저 모델을 사용한다. 그러므로 우리가 만든 모델을 사용하려면 아래와 같이 선언해 주어야 한다. REST_FRAMEWORK
에 대한 설정을 다음과 같이 하면 된다. 주석의 내용과 같이 해도 되고 하지 않아도 당장 프로그램을 돌리는데에는 무방하다. #### 상단 생략 ####
AUTH_USER_MODEL = 'user.User'
# 이 부분은 없어도 상관은 없다. 아래와 같이 세팅하지 않으면 장고가 알아서 기본 세팅을 가져간다.
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [ # 기본적인 view 접근 권한 지정
'rest_framework.permissions.AllowAny'
],
'DEFAULT_AUTHENTICATION_CLASSES': [ # session 혹은 token을 인증 할 클래스 설정
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication'
],
'DEFAULT_PARSER_CLASSES': [ # request.data 속성에 액세스 할 때 사용되는 파서 지정
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
]
}
AttributeError: type object 'User' has no attribute 'USERNAME_FIELD'
from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
# custom user model 사용 시 UserManager 클래스와 create_user, create_superuser 함수가 정의되어 있어야 함
class UserManager(BaseUserManager):
def create_user(self, username, password=None):
if not username:
raise ValueError('Users must have an username')
user = self.model(
username=username,
)
user.set_password(password)
user.save(using=self._db)
return user
# python manage.py createsuperuser 사용 시 해당 함수가 사용됨
def create_superuser(self, username, password=None):
user = self.create_user(
username=username,
password=password
)
user.is_admin = True
user.save(using=self._db)
return user
# custom user model
class User(AbstractBaseUser):
# "사용자 계정"은 admin 페이지에서 나오는 메뉴의 이름 / unique=True를 설정해 단 하나의 값만 입력할 수 있도록 함
username = models.CharField("사용자 계정", max_length=50, unique=True)
password = models.CharField("비밀번호", max_length=128)
email = models.EmailField("이메일", max_length=100)
name = models.CharField("이름", max_length=20)
# auto_now_add : 최초 생성 시의 시간을 자동으로 입력해줌.(그 후 업데이트에 대한 시간 기록은 x) 주로 가입일, 최초 생성일 등에 사용함
join_data = models.DateTimeField("가입일자", auto_now_add=True)
# 사용자 계정이 활성화 되었는지(False이면 비활성화)
is_active = models.BooleanField(default=True)
# admin 권한을 사용할 것인지(is_staff에서 해당 값 사용)
is_admin = models.BooleanField(default=True)
# 사용자가 로그인할 때 사용하는 id로 어떤 것을 사용할래? 라는 것에 지정을 해준 것.
# 물론 다른 값으로도 사용 가능하다!!
USERNAME_FIELD = 'username'
# 슈퍼계정을 생성할 때 입력해야 할 값들을 지정할 수 있음.
# 아래와 같이 사용하지 않아도 되지만, 선언은 해야 함
REQUIRED_FIELDS = []
objects = UserManager()
def __str__(self):
return self.username
# 로그인 사용자의 특정 테이블의 crud 권한을 설정, perm table의 crud 권한이 들어간다.
# admin일 경우 항상 True, 비활성 사용자(is_active=False)의 경우 항상 False
def has_perm(self, perm, obj=None):
return True
# 로그인 사용자의 특정 app에 접근 가능 여부를 설정, app_label에는 app 이름이 들어간다.
# admin일 경우 항상 True, 비활성 사용자(is_active=False)의 경우 항상 False
def has_module_perms(self, app_label):
return True
# admin 권한 설정
@property
def is_staff(self):
return self.is_admin
REQUIRED_FIELDS = ['email', 'name']
를 해주면 다음과 같이 슈퍼유저를 생성할 때 이메일과 이름을 넣어주게 된다.permissions
를 한 번 바꿔서 포스트맨을 실행해보도록 하겠다. user/views.py
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import permissions
class UserView(APIView):
# permission_classes = [permissions.AllowAny] # 누구나 view 접근 가능
permission_classes = [permissions.IsAuthenticated] # 로그인된 사용자만 view 접근 가능
# permission_classes = [permissions.IsAdminUser] # admin 유저만 view 접근 가능
def get(self, request):
return Response({"msg": "get method!!" })
def post(self, request):
return Response({"msg": "post method!!" })
def put(self, request):
return Response({"msg": "put method!!" })
def delete(self, request):
return Response({"msg": "delete method!!" })
views.py
에 추가하고 테스트를 진행해보자.# 로그인 및 로그아웃에 사용
from django.contrib.auth import login, authenticate, logout
class UserAPIView(APIView):
permission_classes = [permissions.AllowAny]
# 로그인
def post(self, request):
username = request.data.get('username', '')
password = request.data.get('password', '')
# 변수 user에는 인증에 성공하면 user가 담기고, 인증 실패하면 None이 담기게 됨
user = authenticate(request, username=username, password=password)
if not user:
return Response({"error": "존재하지 않는 계정 또는 일치하지 않는 비밀번호를 입력하셨습니다."})
login(request, user)
return Response({"msg": "login success!!"})
user/urls.py
를 설정하는 것을 잊지말자!from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('user/', views.UserView.as_view()),
path('login/', views.UserAPIView.as_view()),
]
var xsrfCookie = postman.getResponseCookie("csrftoken");
postman.setGlobalVariable('csrftoken', xsrfCookie.value);
delete
메서드를 활용하면 된다. user/views.py
# 로그인 및 로그아웃에 사용
from django.contrib.auth import login, authenticate, logout
class UserAPIView(APIView):
permission_classes = [permissions.AllowAny]
# 로그인
def post(self, request):
username = request.data.get('username', '')
password = request.data.get('password', '')
# 변수 user에는 인증에 성공하면 user가 담기고, 인증 실패하면 None이 담기게 됨
user = authenticate(request, username=username, password=password)
if not user:
return Response({"error": "존재하지 않는 계정 또는 일치하지 않는 비밀번호를 입력하셨습니다."})
login(request, user)
return Response({"msg": "login success!!"})
# 로그아웃
def delete(self, request):
logout(request)
return Response({"msg": "logout success!!"})
user/urls.py
from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('user/', views.UserView.as_view()),
path('login/', views.UserAPIView.as_view()),
path('logout/', views.UserAPIView.as_view()),
]
user = models.ForeignKey(User, verbose_name="사용자", on_delete=models.CASCADE, unique=True)
를 사용해도 문제는 없다. 하지만 django는 아래와 같은 메세지를 보낸다. 뜻은 야 그거 OneToOneField이랑 별차이 없어. OneToOneField 써.
이다. user.UserProfile.user: (fields.W342) Setting unique=True on a ForeignKey has the same effect as using a OneToOneField.
HINT: ForeignKey(unique=True) is usually better served by a OneToOneField.
OneToOneField
를 사용해서 작성해보았다. user/models.py
# User Profile model
class UserProfile(models.Model):
user = models.OneToOneField(User, verbose_name="사용자", on_delete=models.CASCADE)
introduction = models.TextField("자기소개")
birth = models.DateField("생일")
age = models.IntegerField("나이")
hobby = models.CharField("취미", max_length=50)
def __str__(self):
return f'{self.user.username} 님의 프로필'
user/admin.py
에 다음 코드를 작성해보자.from django.contrib import admin
from .models import User as UserModel, UserProfile as UserProfileModel
# Register your models here.
admin.site.register(UserModel)
admin.site.register(UserProfileModel)
OneToOneField
가 아닌 ForeingKey
로 설정했다면 다음과 같이 두 개의 프로필 설정이 가능하게 된다.OneToOneField
는 하나의 레코드만을 바라볼 수 있도록 설정하는 것이다. user와 user profile의 케이스처럼 하나씩 밖에 존재할 수 없는 경우에 사용하면 된다.user/models.py
# Hobby model
class Hobby(models.Model):
name = models.CharField("취미 이름", max_length=50)
def __str__(self):
return self.name
# User Profile model
class UserProfile(models.Model):
user = models.OneToOneField(User, verbose_name="사용자", on_delete=models.CASCADE)
introduction = models.TextField("자기소개")
birth = models.DateField("생일")
age = models.IntegerField("나이")
hobby = models.ManyToManyField(Hobby, verbose_name="취미")
def __str__(self):
return f'{self.user.username} 님의 프로필'
user/admin.py
를 설정한다. from django.contrib import admin
from .models import User as UserModel, UserProfile as UserProfileModel, Hobby as HobbyModel
# Register your models here.
admin.site.register(UserModel)
admin.site.register(UserProfileModel)
admin.site.register(HobbyModel)