### 1. 모델에 User 추가: 기본 제공하는 django.contrib.auth 사용
class Question(models.Model):
# ...
# [User] 1 ㅡ * [Question] 1 ㅡ * [Choice] 라는 설명인듯 ...?
# related name 설정하면 쿼리 할 때 question_set과 같은 default 이름 대신 다른 이름 사용 가능
owner = models.ForeignKey('auth.User', related_name='questions', on_delete=models.CASCADE, null=True)
# ...
Django Shell 테스트
python manage.py makemigrations
python manage.py migrate
# 0002_question_owner migration 확인
python manage.py shell
# shell
from django.contrib.auth.models import User
from polls.models import *
user = User.objects.first()
# related_name 파라미터에서 정해준 이름 사용하는 것 확인
print(user.questions.all().query)
# 쿼리에 유저 id 조건이 붙음...
# 유저 관리 기능을 chooga 해보자
# ...
from django.contrib.auth.models import User
# ...
class UserSerializer(serializers.ModelSerializer):
# many -> 여러개의 question
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 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
# ...
# ...
# 이 방법에서는 validate 따로 해줘야 함 (중복 아이디 제외)
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)
# ModelSerializer 오버라이드 (run_validations에서 실행됨)
def validate(self, attrs):
if attrs['password'] != attrs['password2']:
raise serializers.ValidationError({'password': 'incorrect confirm pw'})
return attrs
class Meta:
model = User
fields = ['username', 'password', 'password2']
# validator 추가 후에 필요 없어짐
# extra_kwargs = {'password': {'write_only': True}}
# ...
polls_api/views.py
# ...
from polls_api.serializers import * # 수정
# ...
# ...
# list 볼 필요 없고 post 만 하면 되니까 createview로
class RegisterUser(generics.CreateAPIView):
serializer_class = RegisterSerializer
# ...
class QuestionSerializer(serializers.ModelSerializer):
# 없으면 owner 선택지 생김
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = Question
fields = ['id', 'question_text', 'pub_date', 'owner']
# ...
polls_api/permissions.py
# 다른 이용자 글 수정 방지를 위해 custom permission 따로 만든다
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS: # read only related methods
return True
return obj.owner == request.user
polls_api/views.py
# ...
from rest_framework import generics, permissions # 추가
class QuestionList(generics.ListCreateAPIView):
# ...
# auth 없을 때 쓰기 방지
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
# 로그아웃시 입력창이 아예 사라짐
# ...
# session에 유저 추가 (기존 create에 owner 붙여서 오버라이드)
# 여기까지 해야 question에 owner가 붙음
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
# ...
# permission 추가로 다른 이용자가 편집하는 것 방지
permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
# ...
# shell에서
qs = QuestionSerializer(data={'question_text': 'xxx', 'owner': 'aaa'})
qs.is_valid()
qs.validated_data
# 를 살펴보면 owner가 들어가지 않음 (read_only이므로)
# save는 강제로 집행 가능
q = qs.save(id=1000)
q.id
# 그래서 polls_api/views.py에서 perform_create처럼 작동 가능
# (serializer.save(owner=self.request.user))
# 브라우저 데브툴에서 Application - cookies에서 해당 사이트 세션id 확인 가능
# -> postman 테스트에 사용
Postman: Django의 credential 헤더들
CRUD is short for Crush, Ruin, Undermine and Destroy