지난번 Post 모델을 작성했던 부분을 다시 수정해보았습니다.
models.py
from django.db import models
from django.contrib.auth import get_user_model
User = get_user_model()
class Post(models.Model):
id = models.AutoField(primary_key=True, null=False, blank=False)
title = models.CharField(max_length=50)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
user = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE, related_name="post")
content = models.TextField()
def __str__(self):
return self.title
id는 해당 테이블의 기본키로 primary_key를 설정하였고, 자동으로 값을 부여하기 위하여 AutoField를 사용했습니다.
또한 게시물의 작성 시간과 수정시간을 알기 위하여 craeted_at은 auto_now_add를 설정하고, updated_at에는 auto_now를 설정하였습니다.
게시글을 작성한 사람도 있어야 하기 때문에 user를 외래키로 받아왔고 user가 삭제된다면 해당 게시물도 삭제하기 위해
on_delete를 CASCADE 해주었습니다.
그리고 django의 ORM 기능을 활용하기 위하여 user에 related_name을 설정해주었습니다.
* ORM모델은 Query문 없이 django에서 DB와 소통하기 위한 것입니다.
그리고 모델을 다시 정의하였기 때문에 마이그레이션을 다시 진행합니다.
python manage.py makemigrations
python manage.py migrate
마이그레이션을 진행하다
First parameter to ForeignKey must be either a model, a model name, or the string 'self' 해당 에러를 마주쳤는데
분명 외래키 모델을 지정해줬음에도 오류가 생겨 매우 의아했다. 코드를 다시 살펴보니
User = get_user_model() <- 이 부분을 User = get_user_model로 작성했던 것...
코드를 차근차근 살펴보자...
지난번 PostgreSQL CRUD 만들기 포스팅을 보면 https://magicmk.tistory.com/7
마이그레이션 과정에서 오류가 나서 이름을 바꾼 뒤 migrate를 해줬는데 그러지 않고
python manage.py migrate {app 이름} zero
python manage.py migrate {app 이름}
이렇게 진행하면 해결할 수 있습니다.
serializer.py
from .models import Post
from rest_framework import serializers
class PostSerializer(serializers.ModelSerializer):
user = serializers.ReadOnlyField(source = 'user.username')
class Meta:
model = Post
fields = ['id', 'title', 'created_at', 'user', 'content']
뒤에서 설명할 views.py에서 user값을 넘겨받을 것이기 때문에 serializer에서 해당 값을 받습니다.
views.py에서 넘겨준 user에 name을 받기 위하여 source='user.username'으로 설정하였습니다.
views.py
from .models import Post
from .serializer import PostSerializer
from rest_framework import viewsets
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from .permissions import IsOwnerOrReadOnly
class PostViewSet(viewsets.ModelViewSet):
authentication_classes = [BasicAuthentication, SessionAuthentication]
permission_classes = [IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
queryset = Post.objects.all()
serializer_class = PostSerializer
def perform_create(self, serializer):
serializer.save(user = self.request.user)
PostSerializer의 user 필드에 user값을 전달하기 위하여 perform_create 메서드를 추가하였습니다.
ViewSets에서 perform_create를 사용하면 기존의 create 함수를 재정의 할 수 있습니다.
또한 게시글을 작성할 때 user의 내용을 담아오는데 로그인 하지 않은 사용자가 게시글을 작성하면 에러가 발생하기
때문에 authentication과 permission을 사용했습니다.
하지만 그냥 BasicAuthentication, SessionAuthentication, IsAuthenticatedOrReadOnly만 사용한다면
로그인을 한 유저가 아무 게시글이나 수정 또는 삭제 등을 할 수 있기 때문에 개인을 판별하는 것이 필요합니다.
따라서 해당 앱 내부에 permissions.py 파일을 생성합니다.
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.user == request.user
간단하게 SAFE_METHOD (GET,HEAD,OPTIONS) 요청은 허용하고, PUT, PATCH, DELETE 등은 해당 게시글의 user와
로그인되어 있는 user가 동일한 경우만 권한을 허용합니다.
그 뒤 views.py로 돌아가 permission_classes에 추가하면 됩니다.
urls.py
from django.urls import path, include
from .views import PostViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('post', PostViewSet)
urlpatterns = [
path('', include(router.urls))
]
url의 경우에는 특별한 사항은 없습니다.
마지막으로 서버를 실행하여 테스트를 해보면
DB에도 정상적으로 데이터가 들어가는 것을 확인했습니다. 그럼 이상으로 포스팅 끝.
'Python > Django' 카테고리의 다른 글
Django Rest Framework란? (0) | 2022.04.14 |
---|---|
Django + PostgreSQL CRUD 만들기 (0) | 2022.02.23 |
Django와 PostgreSQL 연동 (1) | 2022.02.23 |