1. User 추가 및 관리

  • polls/models.py
    • owner = models.ForeignKey('auth.User', related_name='questions', on_delete=models.CASCADE, null=True) 추가
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)

    @admin.display(boolean=True, description='최근생성(하루기준)')
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
        # 생성된지 하루가 안지났는지

    def __str__(self):
        if self.was_published_recently():
            new_badge = '[NEW]'
        else:
            new_badge = ''
        return f'{new_badge} 제목: {self.question_text}, 날짜: {self.pub_date}'
  • polls_api/serializers.py
from rest_framework import serializers
from polls.models import Question
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']
  • polls_api/views/py
from polls_api.serializers import QuestionSerializer, UserSerializer
from rest_framework import generics
from polls.models import Question
from django.contrib.auth.models import User

class QuestionList(generics.ListCreateAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer


class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer

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

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

스크린샷 2023-11-04 오전 2 40 54

2. Form으로 User 생성

  • polls/view.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')  # urls.py에서 정의했던 이름을 기반으로 url 만들어줌
    template_name = 'registration/signup.html'
  • polls/urls.py
path('signup', SignupView.as_view(), name='signup'),

추가하기

  • polls/templates/registration/signup.html
<h2>회원가입</h2>
<form method="post">
    
    {% csrf_token %}
    {{ form.as_p }}    <!--form 클래스 사용-->  
    
    <button type="submit">가입하기</button>
</form>

스크린샷 2023-11-04 오전 3 10 37

회원가입 페이지

스크린샷 2023-11-04 오전 3 11 40

가입하기를 누르면 회원 리스트 페이지로 넘어감! reverse_lazy('user-list')

3. Serializer로 User 생성

  • polls_api/serializers.py
from rest_framework import serializers
from polls.models import Question
from django.contrib.auth.models import User
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']
        extra_kwargs = {'password' : {'write-only':True}}
  • polls/views.py
from polls_api.serializers import *
from rest_framework import generics

class RegisterUser(generics.CreateAPIView):
    serializer_class = RegisterSerializer
  • polls/urls.py -path('register/', RegisterUser.as_view()), 추가
from django.urls import path
from .views import *

urlpatterns = [
    path('question/', QuestionList.as_view(), name='question-list'),
    path('question/<int:pk>/', QuestionDetail.as_view(), name='question-detail'),
    path('users/', UserList.as_view(), name='user-list'),
    path('users/<int:pk>/', UserDetail.as_view(), name='user-detail'),
    path('register/', RegisterUser.as_view()),
]
  • UserList view에 유저 생성 기능을 넣지 않고, 새로 만드는 이유
    • 생성할 때만 필요한 기능을 따로 serializer로 만들고 싶기 때문!

4. User 권한 관리

로그인, 로그아웃

  • pollst_api/urls.py
    • path('api-auth/', include('rest_framework.urls')), 추가
  • mysite/settings.py
from django.urls import reverse_lazy

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

추가

스크린샷 2023-11-04 오전 9 31 14

스크린샷 2023-11-04 오전 9 31 30

로그인, 로그아웃 버튼 생성됨

권한 관리

  • 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:    # ('GET', 'HEAD', 'OPTIONS') : 읽을 때
            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]

스크린샷 2023-11-04 오전 9 35 51

스크린샷 2023-11-04 오전 9 36 33

스크린샷 2023-11-04 오전 9 44 11

5. perform_create()

  • polls_api/views.py
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)
  • http://127.0.0.1/rest/question/ -> QuestionList view로 이동
  • QuestionList
    • generics.ListCreateAPIView
      • mixins.CreateModelMixin
  • 안에 정의된 메소드들
    • def get(self, request, *args, **kwargs)
    • def create(self, request, *args, **kwargs)
    • def perform_create(self, serializer)
      • 지워짐: mixins.CreateModelMixin
      • 동작함: QuestionList
  • django shell
from polls_api.serializers import QuestionSerializer

question_serializer = QuestionSerializer(data={"question_text":"some text", "onwer":"someone"})
question_serializer.is_valid()
# True

question_serializer.validated_data
# OrderedDict([('question_text', 'some text')])
## owner는 ReadOnly 이기 때문

question = question_serializer.save(id=10000)
question.id
# 10000
## save할 때에는 주어진 값을 그대로 씀

question.question_text
# 'some text'

6. POSTMAN

  • 여러 요청을 저장해두었다가 쉽게 재현해서 사용할 수 있어 API 서버 개발에 자주 사용함!

https://www.postman.com/ 다운로드 링크

PUT 요청

스크린샷 2023-11-05 오전 1 33 42

Headers에 {“Content-Type”:”application/json”}만 지정하고 PUT 요청을 보냈을 때 Authenticatoin credentials were not provided 에러 발생!

스크린샷 2023-11-05 오전 1 33 54

sessionidcsrftoken 값을 지정해주어야 함
웹브라우저에서 [개발자도구]-[애플리케이션] 에서 확인 가능 -> 복붙!

스크린샷 2023-11-05 오전 1 37 47

Headers 구성 후 정보를 잘 가져온 것을 볼 수 있음

GET 요청

스크린샷 2023-11-05 오전 1 38 28

따로 정보를 입력하지 않아도 해당 주소의 정보를 잘 가져온 것을 볼 수 있음