[프로그래머스] 데브코스 데이터엔지니어링 TIL Day 14

주재민·2023년 11월 2일
0
post-thumbnail

📖 학습주제

파이썬 장고 프레임웍을 사용해서 API 서버 만들기(4)


User

User 추가하기

settings.pydjango.contrib.auth가 존재해 유저를 추가하거나 로그인하는 등을 할 수 있다.

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='생성일')  
    owner = models.ForeignKey('auth.User', related_name='questions', on_delete=models.CASCADE, null=True) #on_delete=models.CASCADE : 오너인 유저가 삭제되면 질문도 삭제
                              
    @admin.display(boolean=True, description='최근생성(하루기준)')
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
    
    def __str__(self):
        return f'제목: {self.question_text}, 날짜: {self.pub_date}'

questions라고 지정된 related_name을 통해 auth.User 모델에서 Question 모델을 참조할 때 questions로 참조가 가능하다.

User 관리하기

시리얼라이저로 유저를 관리하는 모델을 만들어보자.

polls_api/serializers.py

from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):
    questions = serializers.PrimaryKeyRelatedField(many=True, queryset=Question.objects.all())
    
    class Meta:
        model = User
        fields = ['id', 'username', 'questions']

여기서 questions는 User 모델의 Primary Key 값으로 연결된 모든 Question 모델의 오브젝트들을 가져오는 기능을 한다. questions가 가져오는 정보는 User 테이블에 있는 것이 아니기 때문에 해당 코드가 필요하다.

polls_api/views.py

from django.contrib.auth.models import User
from polls_api.serializers import UserSerializer

class UserList(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    
class UserDetail(generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

path를 추가하자.

polls_api/urls.py

from django.urls import path
from .views import *

urlpatterns = [
    path('question/', QuestionList.as_view(), name='question-list'),
    path('question/<int:pk>/', QuestionDetail.as_view()),
    path('users/', UserList.as_view(),name='user-list'),
    path('users/<int:pk>/', UserDetail.as_view()),

]

User 생성하기

두 가지 방법으로 유저를 생성해보자

Form 이용

장고에서는 유저를 생성할 수 있는 UserCreationForm이라는 메소드를 제공한다.

polls/views.py

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
    success_url = reverse_lazy('user-list')
    template_name = 'registration/signup.html'

reverse_lazy() : 이름을 통해 URL으로 접근 가능

polls/templates/registration/signup.html

<h2>회원가입</h2>
 <form method="post">
   {% csrf_token %}
   {{ form.as_p }}
   <button type="submit">가입하기</button>
</form>

path를 추가하자

polls/urls.py

from django.urls import path
from . import views
from .views import *

app_name = 'polls'
urlpatterns = [
    path('',views.index, name='index'),
    path('<int:question_id>/', views.detail, name='detail'),
    path('<int:question_id>/vote/', views.vote, name='vote'), 
    path('<int:question_id>/result/', views.result, name='result'), 
    path('signup/', SignupView.as_view(), )
]

Serializer 이용

polls_api/serializers.py

class RegisterSerializer(serializers.ModelSerializer):
    # 너무 간단한 password 방지
    password = serializers.CharField(write_only=True, required=True, validators=[validate_password])
    password2 = serializers.CharField(write_only=True, required=True)
    
    # 위의 두 password가 일치하는지 확인
    def validate(self, attrs):
        if attrs['password'] != attrs['password2']:
            raise serializers.ValidationError({"password": "두 패스워드가 일치하지 않습니다."})
        return attrs
        
    # 유저모델에는 password2라는 필드가 없어 별도로 create 구현
    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/views.py

from polls_api.serializers import RegisterSerializer

class RegisterUser(generics.CreateAPIView):
    serializer_class = RegisterSerializer

path를 추가하자

polls_api/urls.py

from django.urls import path
from .views import *

urlpatterns = [
    path('question/', QuestionList.as_view(), name='question-list'),
    path('question/<int:pk>/', QuestionDetail.as_view()),
    path('users/', UserList.as_view(),name='user-list'),
    path('users/<int:pk>/', UserDetail.as_view()),
    path('register/', RegisterUser.as_view()),

]

User 권한 관리

유저가 로그인하고 로그인한 유저가 question을 만들 때 누가 만들었는지 기록하는 기능을 구현해보자.

기록하는 기능은 models.py에서

owner = models.ForeignKey('auth.User', related_name='questions', on_delete=models.CASCADE, null=True)

라고 된 owner 필드에 로그인한 사용자 id를 넣어주면 된다. 이를 위해 로그인 기능을 구현하자.

먼저 path를 추가하자.

polls_api/urls.py

from django.urls import path,include
from .views import *

urlpatterns = [
    path('question/', QuestionList.as_view(), name='question-list'),
    path('question/<int:pk>/', QuestionDetail.as_view()),
    path('users/', UserList.as_view(),name='user-list'),
    path('users/<int:pk>/', UserDetail.as_view()),
    path('register/', RegisterUser.as_view()),
    path('api-auth/', include('rest_framework.urls'))

]

settings.py에서 로그인/로그아웃 후 redirect되는 URL을 정의해주자.

mysite/settings.py

from django.urls import reverse_lazy

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

QuestionSerializer를 수정해 질문을 작성한 유저가 owner가 되도록 하자.

polls_api/serializers.py

class QuestionSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')

    class Meta:
        model = Question
        fields = ['id', 'question_text', 'pub_date', 'owner']

질문을 만든다고 해서 owner가 자동 지정이 되는 것은 아니기 때문에 그 기능을 구현하자.

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

로그아웃 했을 때 질문을 작성하지 못하도록 코드를 작성하자.

polls_api/views.py

from rest_framework import generics,permissions
from .permissions import IsOwnerOrReadOnly

class QuestionList(generics.ListCreateAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer
    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]

POSTMAN

API서버가 동작하는 상황에서는 로그인 정보 없이 요청이 들어오기도 하는데 이런 상황을 확인하기 위해 POSTMAN을 이용한다.

0개의 댓글