: Model의 각 클래스에 소유권을 추가하고 싶은 경우에 사용한다. 예제에서는 각 질문에 대하여 소유자를 확인 하도록 하는 필드를 생성하였다.
owner = models.ForeignKey('auth.User',related_name='questions',on_delete=models.CASCADE, null =True)
: User를 활용하여 다양한 동작을 하기 위해서는 통신이 필요하다. 이 때 사용되는 Serializer를 생성하고 View에 등록해준다.
# serializer.py
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
# user의 pk를 통해서 여러개의 questions를 갖는다는 것을 명시.
# question을 통해서 가져오기 때문에 다음처럼 사용
questions = serializers.PrimaryKeyRelatedField(many=True, queryset=Question.objects.all())
class Meta:
model =User
fields = ['id','username','questions']
#views.py
class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
💡 PrimaryKeyRelatedField에 대한 이해
: PrimaryKeyRelatedField는 기본 키를 사용하여 관계의 대상을 나타낼 수 있다. 이에 예시는 아래 코드와 같다.
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Album
fields = ['album_name', 'artist', 'tracks']
# 결과
{
'album_name': 'Undun',
'artist': 'The Roots',
'tracks': [
89,
90,
91,
...
]
}
참고 : https://www.django-rest-framework.org/api-guide/serializers/ (Django REST framework api guide)
: from django.contrib.auth.forms import UserCreationForm
을 활용하여 구현해준다.
from django.views import generic
from django.urls import reverse_lazy
from django.contrib.auth.forms import UserCreationForm
class SignupView(generic.CreateView):
form_class = UserCreationForm
# reverse_lazy를 사용하여 다른 Path로 전송시킬 수 있다.
success_url = reverse_lazy('user-list')
template_name = 'registration/signup.html'
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username','password']
extra_kwargs = {'password' : {'write_only':True}}
from polls_api.serializers import RegisterSerializer
class RegisterUser(generics.CreateAPIView):
serializer_class = RegisterSerializer
path('register/', RegisterUser.as_view()),
# 간단한 패스워드 막기
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
# 정상적으로 패스워드가 동일한 경우에 save를 해준다.
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']
: Question에 owner 필드를 넣어주었지만, 여전히 로그인 하지 않은경우, 소유자가 아닌 다른 계정의 경우에도 편집이 가능하다. 따라서 적절한 권한부여를 통해 이를 막아야한다.
# urls.py
path('api-auth/', include('rest_framework.urls'))
# settings.py에서 작업한다.
from django.urls import reverse_lazy
LOGIN_REDIRECT_URL = reverse_lazy('question-list')
LOGOUT_REDIRECT_URL = reverse_lazy('question-list')
class QuestionSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = Question
fields = ['id', 'question_text', 'pub_date', 'owner']
# Views.py
from rest_framework import generics,permissions
class QuestionList(generics.ListCreateAPIView):
...
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
# save 요청시에는 어떤 값이는 문제 없이 넣을 수 있다.
serializer.save(owner=self.request.user)
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
: request.method가 SAFE_METHODS ('GET', 'HEAD', 'OPTIONS')에 포함되거나, obj의 user가 로그인 된 user와 같으면 API의 요청 결과를 볼 수 있다.
실제 api가 동작하는 서버에서 로그인 정보없이 요청이 들어올 수 있다. 따라서 다양한 상황에 대하여 API서버를 테스트 해보아야 한다.
이를 PostMan이 수행해주는데 URL과 함께 API 동작을 지정해 줄 수 있고, Headers를 통해서 각 key를 입력 가능하다.
: Form과 Serializer를 활용하여 User를 생성하고, 권한관리를 직접 해보면서 다양한 상황이 발생한 경우에 이에 대한 권한 처리가 중요하다는 것을 알게 된 것 같다. 더욱이 아직 다루어보지 못한 여러 기능에 대해서 공부가 더 필요하다 생각한다.