[4/11] TIL - Django를 이용한 API 만들기 [4]

Sangwon Jwa·2024년 4월 11일

데브코스 TIL

목록 보기
14/54
post-thumbnail

📖 학습 주제


  1. User 추가
  2. User 관리
  3. Form을 사용하여 User 생성
  4. Serializer를 사용하여 User 생성
  5. User 권한 관리
  6. POSTMAN

✏️ 주요 메모 사항 소개


사용자(Users) & 인증(Authentication)

오늘은 앞서 작성한 Question에 작성자를 만들고 작성자만 Question을 수정, 삭제할 수 있도록 만들어보자.

User 추가하기

  • 작성자를 추가하기 위해 Question 모델의 필드에 owner 변수를 추가한다
# polls.models.py
class Question(models.Model):
    question_text = models.CharField(max_length=200, verbose_name='질문')
    pub_date = models.DateTimeField(auto_now_add=True, verbose_name='생성일')
    
    # related_name을 지정하면 User에서 questions라는 이름으로 Question들을 가져올 수 있다.
    # related_name을 지정하지 않으면 Django에서 자동적으로 '모델명_set'을 구현해주기 때문에 그 이름으로 사용할 수 있다.
    owner = models.ForeignKey('auth.User', related_name='questions', on_delete=models.CASCADE, null=True)
    
    # ...

User 관리하기

  • User에 대해 serializer를 만들고 view를 구현
  1. polls_api/serializers.py 파일에 UserSerializer 작성
class UserSerializer(serializers.ModelSerializer):
    # question들을 가져오는 필드의 정보는 User 테이블에 있는게 아니기 때문에 따로 불러오는 처리가 필요 
    questions = serializers.PrimaryKeyRelatedField(many=True, queryset=Question.objects.all())

    class Meta:
        model = User
        fields = ('id', 'username', 'questions')
  1. polls_api/views.py : User에 대한 View 작성
class UserList(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

class UserDetail(generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
  1. URL 경로 설정 추가
urlpatterns = [
    
    #...
    
    path('users/', UserList.as_view()),
    path('users/<int:pk>/', UserDetail.as_view()),
]


Form을 이용해 유저 생성

[장고에서 제공하는 기능으로 구현]

  1. polls/views.py 파일에 SignupView 클래스 추가
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는 url에서 정의한 name을 기반으로 url을 가져오는 함수
    success_url = reverse_lazy('user-list')
    template_name = 'registration/signup.html'
  1. signup.html 템플릿 작성
<h2>회원가입</h2>
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">가입하기</button>
</form>
  1. polls/urls.py 에서 URL 연결
app_name = 'polls'
urlpatterns = [
    
    # ...
    
    path('signup/', SignupView.as_view(), name='signup'),
]


[장고 REST_framework의 serializer를 이용해 구현]

  1. polls_api/serializers.py 에 RegisterSerializer 클래스 작성
class RegisterSerializer(serializers.ModelSerializer):
    # 패스워드에 대한 설정 (validate_password를 이용하여 password의 유효성을 검사)
    password = serializers.CharField(write_only=True, required=True, validators=[validate_password])
    password2 = serializers.CharField(write_only=True, required=True)
    
    # 자체 유효성 검사를 위한 validate 메서드 구현
    def validate(self, attrs):
        if attrs['password']!= attrs['password2']:
            raise serializers.ValidationError({'password': '비밀번호가 일치하지 않습니다.'})
        return attrs

    # User 객체에는 password2 가 없기 때문에 따로 create 메서드 구현
    def create(self, validated_data):
        user = User.objects.create_user(username=validated_data['username'])
        user.set_password(validated_data['password'])
        user.save()

        return user

    class Meta:
        model = User
        fields = ('username', 'password', 'password2')
        
  1. polls_api/views.py에 RegisterUser 뷰 작성
# ...

class RegisterUser(generics.CreateAPIView):
    serializer_class = RegisterSerializer
  1. polls_api/urls.py URL 설정
urlpatterns = [
    
    # ...
    
    path('register/', RegisterUser.as_view(), name='register'),
]


User 권한 관리 (사용자가 작성한 질문만 출력)

  1. polls_api/urls.py에 로그인 처리를 할 url 설정
urlpatterns = [
    # ...
    
    path('api-auth/', include('rest_framework.urls')),
]

 

  1. mysite.settings.py 에서 로그인 성공시 리다이렉트 되는 url을 설정.
# ...

from django.urls import reverse_lazy

LOGIN_REDIRECT_URL = reverse_lazy('question-list')
LOGOUT_REDIRECT_URL = reverse_lazy('question-list')

# ...


  1. serializer를 수정해서 질문을 만들면 로그인한 사용자가 질문의 owner가 되도록 설정
# polls_api/serializers.py

class QuestionSerializer(serializers.ModelSerializer):
	# 작성자를 나타낼 owner 변수 추가
    owner = serializers.ReadOnlyField(source='owner.username')
    class Meta:
        model = Question
        fields = ('id', 'question_text', 'pub_date', 'owner')

 

  1. 사용자의 권한 설정 (polls_api/view.py)
class QuestionList(generics.ListCreateAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer
    # 로그인이 된 상태에서만 question 생성 가능
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    # create할 때 owner필드를 현재 접속한 유저로 설정
    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]

 

  1. permissons에 있지 않는 커스텀 권한을 따로 설정 (polls_api/permissions.py)
from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
    
    	# 조회 등의 조작이 일어나지 않는 http 메서드는 그냥 통과
        if request.method in permissions.SAFE_METHODS:
            return True
    	
        # 그 외의 메서드는 request를 보낸 유저와 대상의 owner로 설정된 유저가 같은지 유효성 검사
        return obj.owner == request.user

POSTMAN

  • API의 작동 과정을 확인할 수 있는 프로그램으로 URL과 Body를 설정해서 API의 응답을 확인해 볼 수있다.

💦 공부하며 어려웠던 내용


여러 라이브러리를 사용해서 클래스가 많아지고 클래스 간의 관계가 복잡해져서 알아보고 이해하는 것이 가장 어려웠던 것 같다. 자동적으로 메서드를 생성하거나 데이터를 연결해주는 기능들은 편하긴 하지만 충분한 이해가 선행되어야 할 것 같다는 생각이 들었다.

0개의 댓글