User 추가하기
User 관리하기
Form을 사용하여 User 생성하기
Serializer를 사용하여 User 생성하기
User 권한 관리
상속(Inheritance)과 오버라이딩(Overriding)
perform_create()
POSTMAN
Question 객체를 관리할 User 추가
polls/models.py
...
class Question(models.Model):
...
# owner라는 User 객체는 여러 개의 Question 객체를 가짐
# User 객체에서 Question 객체를 접근할 때 "questions"라는 이름을 사용
# owner가 삭제되면 Question 객체도 삭제(cascade)
owner = models.ForeignKey("auth.User", related_name = "questions", on_delete = models.CASCADE, null = True)
...
User에 대한 Serializer를 생성하고, view 구현
polls_api/serializers.py
...
class UserSerializer(serializers.ModelSerializer):
# User를 참조하는 Question 객체를 PrimaryKeyRelatedField로 연결
questions = serializers.PrimaryKeyRelatedField(many = True, queryset = Question.objects.all())
class Meta:
model = User
fields = ["id", "username", "questions"]
polls_api/urls.py
...
urlpatterns = [
...
path('users/', UserList.as_view()),
path('users/<int:pk>', UserDetail.as_view()),
]
polls_api/views.py
...
from polls_api.serializers import QuestionSerializer, UserSerializer
from django.contrib.auth.models import User
...
class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
Django에서 제공하는 회원가입 form을 사용해 회원가입 기능 구현
polls/views.py
...
from django.urls import reverse, reverse_lazy
from django.views import generic
from django.contrib.auth.forms import UserCreationForm
...
class SignUpView(generic.CreateView):
form_class = UserCreationForm
# 사용자 생성 시 user-list 페이지로 이동
success_url = reverse_lazy("user-list")
template_name = "registration/signup.html"
reverse_lazy()
: urls.py에 정의된 name을 사용해 URL로 이동polls/urls.py
...
from views import *
urlpatterns = [
...
path('signup/', SignUpView.as_view())
]
from lib import *
vs import lib
from lib import *
: method()
와 같이 호출import lib
: lib.method()
와 같이 호출polls_api/urls.py
...
urlpatterns = [
...
path('users/', UserList.as_view(), name = "user-list"),
...
]
templates/registration/signup.html
<h2>회원가입</h2>
<form method = "post">
{% {% csrf_token %} %}
<!--SignUpView 클래스에 정의된 UserCreationForm을 불러와서 사용-->
{{ form.as_p }}
<button type = "submit">가입하기</button>
</form>
비밀번호를 검증하고, 사용자를 생성하는 로직을 구현해야 하기 때문에 UserSerializer를 사용하지 않고 별도의 RegistrationSerializer 구현
polls_api/serializers.py
...
from django.contrib.auth.password_validation import validate_password
...
class RegisterSerializer(serializers.ModelSerializer):
# 패스워드 검증
password = serializers.CharField(write_only = True, required = True, validators = [validate_password])
password2 = serializers.CharField(write_only = True, required = True)
def validate(self, attrs):
if attrs["password"] != attrs["password2"]:
raise serializers.ValidationError({"password": "두 패스워드가 일치하지 않습니다."})
return attrs
def create(self, validated_data):
user = User.objects.create(username = validated_data["username"])
user.set_password(validated_data["password"])
user.save()
return user
class Meta:
model = User
fields = ["username", "password", "password2"]
polls_api/urls.py
...
urlpatterns = [
...
path('register/', RegisterUser.as_view()),
]
polls_api/views.py
from polls_api.serializers import *
...
class RegisterUser(generics.CreateAPIView):
queryset = User.objects.all()
serializer_class = RegisterSerializer
로그인한 사용자만 질문을 작성할 수 있고, 질문의 작성자만 해당 질문을 수정, 삭제할 수 있는 기능 구현
project/settings.py
...
from django.urls import reverse_lazy
LOGIN_REDIRECT_URL = reverse_lazy("question-list")
LOGOUT_REDIRECT_URL = reverse_lazy("question-list")
...
polls_api/urls.py
...
urlpatterns = [
...
path('api-auth/', include('rest_framework.urls')),
]
polls_api/views.py
from rest_framework import generics, permissions
from .permission import IsOwnerOrReadOnly
class QuestionList(generics.ListCreateAPIView):
...
# 로그인하지 않았다면 질문 작성 불가
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
# 질문 생성 시 작성자 저장
def perform_create(self, serializer):
serializer.save(owner = self.request.user)
class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
# 질문 작성자가 아니면 질문 수정 불가
permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
...
polls_api/permissions.py
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
# 정보를 읽는 요청은 허용
if request.method in permissions.SAFE_METHODS:
return True
return obj.owner == request.user
상속: 하나의 클래스가 다른 클래스로부터 메서드와 속성을 그대로 물려받아 사용할 수 있는 기능 제공
부모 클래스(= 상위 클래스): 메서드와 속성을 물려주는 클래스
자식 클래스(= 하위 클래스): 메서드와 속성을 물려받는 클래스
장점
코드의 재사용성 증가
클래스 간의 계층 구조를 만들어 클래스들을 그룹화해서 관리 가능
views.py에서 각 클래스를 generics.XXXAPIView를 상속받아서 만들었기 때문에 get()
, post()
등의 메서드를 정의하지 않고도 사용 가능한 것
Serializer.save()
메서드는 read_only인 필드도 매개변수로 사용 가능