이번엔 전에 작업했던 작업물을 사용하여 권한을 알아봅시다
polls의 question모델을 수정해준뒤
from django.db import models
from django.contrib import admin
from django.utils import timezone
import datetime
# Create your models here.
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 :
return f'[NEW!!]{self.question_text}'
return self.question_text
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return f'[{self.question.question_text}]{self.choice_text}'
마이크래이션을 진행해줍니다
모델에 owner가 추가되었는데 owner는 user를 참조키로 받으며 해당 참조를한 데이터가 삭제되면 question또한 같이 삭제가되게 설정되어있습니다 null값이 대신들어가는것 또한 가능하네요
serializer도 수정해줍니다
from rest_framework import serializers
from polls.models import Question
from django.contrib.auth.models import User
class QuestionSerializer(serializers.ModelSerializer) :
class Meta:
model = Question
fields=['id','question_text', 'pub_date']
def create(self, validated_data):
return Question.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.question_text = validated_data.get('question_text', instance.question_text)
instance.save()
return instance
class UserSerializer(serializers.ModelSerializer):
question = serializers.PrimaryKeyRelatedField(many=True, queryset=Question.objects.all())
class Meta :
model = User
fields = ['id','username','question']
views 또한 수정해줍시다
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
유저 목록과 유저 디테일을 들어갔을때 정보가 잘 출력되도록 view를 작업해줍니다
이제 urls를 수정하여 접근할수 있게 만들어줍시다
urlpatterns = [
path('question/', question_list, 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'),
]
이제 서버를 실행하고 주소/rest/users로 접근하면 유저 정보를 볼수있게되었습니다
이번에는 유저를 추가하기위한 회원가입을 간단하게 구현해봅시다
회원가입 부분을 처리해줄 polls의 view를 작업해줍니다
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'
form_class는 회원가입화면을 처리하는데 도움을줄것이고
success_url 성공시 user-list로 페이지를 이동시킬것이며
template_name 은 간단하게 보여줄 화면을 뜻합니다
이번에는 회원가입을 처리해줄 화면을 작업해줍시다
<h2>회원가입</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">가입하기</button>
</form>
UserCreationForm을 이용해서 form.as_p로 간단하게 회원가입폼을 처리할수있게되었습니다
urls를 처리해줍시다
from .views import SignupView
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())
]
SignupView사용하기 위해 views 안에있는 SignupView를 import해줍시다
이제 회원가입 페이지 작업이 끝났습니다
회원가입을 이제 회원가입에 성공하면 rest/users 로 이동하게되어 회원목록을 보여줄것입니다
이번에는 이 기능을 시리얼라이저로 처리해봅시다
일단은 serializer를 추가합니다
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']
password의 값을 validate_password가 통과하는지 확인한뒤 받아옵니다
password2의 값을 받아옵니다
validate에서 password와 password2가 맞는지 확인후 틀릴시 validationError를 반환합니다
맞으면 create를 한뒤 user정보를 return합니다
mata로 model은 User를 사용하고 field는 username, password, password2를 사용합니다
이제 views도 작업해줍시다
from polls_api.serializers import RegisterSerializer
class RegisterUser(generics.CreateAPIView):
serializer_class = RegisterSerializer
마지막으로 해당기능에 접근하기 위한 urls또한 작업해줍시다
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()),
]
이제 서버에서 확인을하면 시리얼라이저로 회원가입 기능을 구현한것을 확인할수 있습니다
유저가 생겼으니 이번엔 로그인기능을 만들어봅시다
api의 urls로 가서 로그인할때 사용할 path를 추가해줍니다
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'))
]
이제 프로젝트의 setting으로가서 로그인과 로그아웃의 redirect를 설정해줍니다
from django.urls import reverse_lazy
LOGIN_REDIRECT_URL = reverse_lazy('question-list')
LOGOUT_REDIRECT_URL = reverse_lazy('question-list')
이제 로그아웃과 로그인후 question-list의 name을 가진 path로 redirect시켜줍니다
api파일에 permission을 다룰 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
내용은 간단하게 본인의 user정보와 글의 owner가 같지않으면 수정권한을 주지않는 permission입니다
api의 views로 가서 questionList와 Detail에 user로그인여부와 본인이 쓴글만 수정할수있게 permission을 붙여줍니다
from rest_framework import generics, permissions
from .permissions import *
class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
permission_classes = {permissions.IsAuthenticatedOrReadOnly, 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)
permission_classes 를 이용하여 로그인 여부와 본인글 여부를 확인하는 부분을 만들어줍시다
list에서는 question을 생성할때 owner에 user의 정보가 들어가게 수정해줍니다
save를 할때는 값을 전달해서 사용할수 있기때문에 serializer.save(owner=self.request.user)를 보면 readonly값인 owner의 값을 전달하여 유저의 네임을 가져다쓸수 잇습니다
시리얼라이즈 파일도 owner의 정보를 받을수 있게 수정을해줍시다
class QuestionSerializer(serializers.ModelSerializer) :
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = Question
fields=['id','question_text', 'pub_date', 'owner']
이제 서버를 켜서 지금까지 구현한 기능들을 확인해봅시다
로그인도 잘되고 로그인 여부와 권한에 따른 페이지의 변화도 확인할수 있습니다
포스트맨이란?
API 개발 및 테스트에 사용되는 도구중 하나입니다
포스트맨의 장점