각 CRUD에 대해 url을 각각 만들어주는 것이 아니라, url에 대해 HTTP method를 설정하고 view를 그에 따라 수정해주면서 하면 됨.
@api_view() : DRF에서 필수로 작성!
() 안에 아무것도 적지 않으면 GET만. 이외에는 ["POST", "PUT"] 등의 리스트로 명시해 주어야 함.
Class Based View 특징
간단한 로직을 처리할 때는 FBV가 편함
나눠서 해야 할 때, 다른 곳에서 상속이 필요할 때는 CBV가 편함
같은 코드 FBV vs. CBV 비교
↓ FBV로 작성했을 때
@api_view(["GET", "POST"]) # 데코레이터: wrapping method. 함수의 실행 전 후에 로직을 더해 줄 수 있음. 달아주는 것만으로도 작동함. 함수형 뷰는 필수 작성(안 쓰면 에러 발생) FBV / 클래스형 뷰도 있다. CBV
def article_list(request):
if request.method == "GET":
# 데이터 가져오기
articles = Article.objects.all()
# serialize
serializer = ArticleSerializer(articles, many=True)
# response 돌려주기
return Response(serializer.data)
elif request.method == "POST":
serializer = ArticleSerializer(data = request.data)
if serializer.is_valid(raise_exception = True): # serializer.errors 를 대신해줄 수 있음.
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED) # 잘 생성 시 돌려줄 status 정해줌
↓ CBV로 작성했을 때
class ArticleListAPIView(APIView):
def get(self, request):
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data)
def post(self, request):
serializer = ArticleSerializer(data = request.data)
if serializer.is_valid(raise_exception = True):
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
※ 코드가 두 번 이상 반복될 경우 CBV에서 함수를 하나 정의해주어 정돈할 수 있음
class ArticleDetailAPIView(APIView):
def get(self,request, pk):
** article = get_object_or_404(Article, pk = pk)
serializer = ArticleSerializer(article)
return Response(serializer.data)
def put(self, request, pk):
** article = get_object_or_404(Article, pk=pk) # 여기 반복
serializer = ArticleSerializer(article, data=request.data, partial=True)
if serializer.is_valid(raise_exception = True):
serializer.save()
return Response(serializer.data)
def delete(self, request, pk):
** article = get_object_or_404(Article, pk = pk) # 여기 반복
article.delete()
return Response(status = status.HTTP_204_NO_CONTENT)
///각 함수에 article = get_object_or_404(Article, pk=pk) 가 반복됨///
따라서
def get_object(self, pk):
return get_object_or_404(Article, pk=pk)
///이렇게 함수를 새로 생성해주어 클래스에 더해준 뒤, 각 행을 바꾸어주면 됨///
최종본은 이렇게
class ArticleDetailAPIView(APIView):
def get_object(self, pk): # 반복되는 코드를 줄여주기 위해 생성
return get_object_or_404(Article, pk=pk)
def get(self,request, pk):
** article = self.get_object(pk) **
serializer = ArticleSerializer(article)
return Response(serializer.data)
def put(self, request, pk):
** article = self.get_object(pk) **
serializer = ArticleSerializer(article, data=request.data, partial=True)
if serializer.is_valid(raise_exception = True):
serializer.save()
return Response(serializer.data)
def delete(self, request, pk):
** article = self.get_object(pk) **
article.delete()
return Response(status = status.HTTP_204_NO_CONTENT)
댓글 CRUD
작성 순서
Articles의 model.py에 Comment 모델을 작성
→ 요구되는 항목 작성 content, created_at, updated_at, article에 대한 ForeignKey
→ 마이그레이션 python manage.py + makemigrations, migrate
→ serializer.py 에 CommentSerializer 작성
→ python manage.py seed articles --number=20 으로 article과 comment 생성
→ 특정 article에 댓글을 생성하려면:
python manage.py seed articles --number=20 --seeder "Comment.article_id" 1
로 article id 1번에 20개의 랜덤 comment를 생성함
CRUD는 위의 article CRUD와 거의 같다.
다만 하면서 겪었던 문제가 있어 언급하자면 comment 삭제에 있어 Method \"GET\" not allowed. 라는 결과값이 나오고 삭제가 되지 않았다.


그래서 urls.py의 'comments/<int:comment_pk>/' 앞에 슬래시도 넣어보았지만,
노란색 글씨로 나온 메세지처럼 맨 앞의 / 는 불필요하다고 나왔다.
GET method가 없다고 해서 delete가 GET이 없어서 그런건가 검색을 해보고, get_object()를 만들어보기도 하고, delete 메소드 중간에 print(comment)를 넣어 get_object_or_404 는 되나 보았는데 print도 되지 않음. 결과적으로 delete() 를 불러오지 못한 것 같아 30분 이상 머리를 싸매다가 댓글 수정으로 넘어갔다.
실마리는 수정을 구현하다가 나왔다.
PUT 을 불러오다가 또 에러가 뜨길래 살펴보았더니 URL의 끝이 /로 끝나지 않아서 장고가 redirect를 하지 못한다는 것.

그래서 끝에 trailing slash를 붙여주었고, 삭제도 보니 끝에 /가 없어 붙여주었더니 바로 그대로 실행되었다.🤦♀️🤦♀️
스펠링이 틀린 곳이 있나 눈이 빠지게 보았는데 결국 마지막의 trailing slash 때문이라니.. 장고 시작하면서 튜터님께서 말씀해주신 것을 몸소 체험하는 기회가 되었다. trailing slash 너 중요한 애였구나...