POST, GET, PUT, DELETE

METHOD 역할
POST POST를 통해 해당 URI를 요청하면 리소스를 생성합니다.
GET GET를 통해 해당 리소스를 조회합니다. 리소스를 조회하고 해당 도큐먼트에 대한 자세한 정보를 가져온다.
PUT PUT를 통해 해당 리소스를 수정합니다.
DELETE DELETE를 통해 리소스를 삭제합니다.

 

다음과 같은 식으로 URI는 자원을 표현하는 데에 집중하고 행위에 대한 정의는 HTTP METHOD를 통해 하는 것이 REST한 API를 설계하는 중심 규칙입니다.

 

 

 

 

HTTP 응답 상태 코드

상태코드
200 클라이언트의 요청을 정상적으로 수행함
201 클라이언트가 어떠한 리소스 생성을 요청, 해당 리소스가 성공적으로 생성됨(POST를 통한 리소스 생성 작업 시)
상태코드
400 클라이언트의 요청이 부적절 할 경우 사용하는 응답 코드
401 클라이언트가 인증되지 않은 상태에서 보호된 리소스를 요청했을 때 사용하는 응답 코드

(로그인 하지 않은 유저가 로그인 했을 때, 요청 가능한 리소스를 요청했을 때)
403 유저 인증상태와 관계 없이 응답하고 싶지 않은 리소스를 클라이언트가 요청했을 때 사용하는 응답 코드

(403 보다는 400이나 404를 사용할 것을 권고. 403 자체가 리소스가 존재한다는 뜻이기 때문에)
405 클라이언트가 요청한 리소스에서는 사용 불가능한 Method를 이용했을 경우 사용하는 응답 코드
상태코드
301 클라이언트가 요청한 리소스에 대한 URI가 변경 되었을 때 사용하는 응답 코드

(응답 시 Location header에 변경된 URI를 적어 줘야 합니다.)
500 서버에 문제가 있을 경우 사용하는 응답 코드

 

 

 

 

Django REST framwork

 

설치하기

pip install django
pip install djangorestframework

 

프로젝트 생성

django-admin startproject 프로젝트명

 

 

settings.py

INSTALLED_APPS = ['rest_framework', 'articles']

 

 

articles - models.py

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

 

python manage.py makemigrations
python manage.py migrate

 

articles - admin.py

from django.contrib import admin
from .models import Article
admin.site.register(Article)

 

pjt - urls.py

	urlpatterns = [
	    path('admin/', admin.site.urls),
	    path('api/v1/', include('articles.urls')),
]

 

 

articles- urls.py

	urlpatterns = [
	    path('articles/', views.article_list),

app_name 없고 path안에 name 속성도 없다. name 사용은 dtl(Django template language)안에서 쓰거나, redirect 사용했었다.

화면 다시 요청하는 redirect 지금 필요없기 때문에 사용하지 않는다.

api/v1/articles/ 연결될 것이다.

 

 

 

articles- views.py

	def article_list(request):
    pass

 

serialize : 파이썬 객체 데이터 json 문자열로 표현…

모델 객체를 쉽게 serialize 해주는 ModelSerializer Modelform이랑 비슷한 역할을 한다.

CRUD 데이터가 유효한지 확인하는 기능 (form.is_valid()) 처럼…

 

 

articles - serializers.py

from rest_framework import serializers
from .models import Article


class ArticleSerializer(serializers.ModelSerializer):
   
	 class Meta:
        model = Article
        fields = '__all__'

ModelSerializer 상속받는 serializer 만든다.

rest_framework 사용하려면 인터프리터에 djangorestframework 설치되어있어야 한다.

이것을 설치한 venv 인터프리터로 선택해야 한다. (파이썬 venv 실행과는 별개)

client 부터 데이터가 제대로 입력되었는지 확인하고, DB 저장하고, model 인스턴스를 json으로 변경하는 역할을 한다.

모든 필드를 검사하겠다. all

 

articles - views.py

from rest_framework.decorators import api_view
from rest_framework.response import Response
from .serializers import ArticleSerializer

@api_view(['GET'])

def article_list(request):
    articles = Article.objects.all()
    serializer = ArticleSerializer(articles, many=True)
    return Response(serializer.data)

객체가 여러 개임을 표현하기 위해 many=True 추가해야한다.

serializer 라는 인스턴스를 만들었다. 인스턴스의 data response하면된다.

view 함수 목적 : HTTP response 만들어낸다.

 

 

@api_view()

DRF view 함수가 응답해야 하는 http 메서드 목록 받음

기본적으로 GET메서드만 허용되며 다른 메서드 요청에 대해서는 405 오류

api/v1/articles/ 에서 데이터 json 확인할 있다. status 200 OK

 

 

articles - urls.py

    path('articles/<int:article_pk>/', views.article_detail),

 

 

articles- views.py

@api_view(['GET'])
def article_detail(request, article_pk):
    article = Article.objects.get(pk=article_pk)
    serializer = ArticleSerializer(article)
    return Response(serializer.data)

하나의 객체를 가져오기 때문에 many=True 안함.

api/v1/articles/1/ 에서 첫번째 게시글의 json 파일을 확인할 있다.

존재하지 않는 게시글 번호로 가면, DoesNotExist 에러가 발생한다.

 

from django.shortcuts import get_object_or_404, get_list_or_404

@api_view(['GET'])
def article_detail(request, article_pk):
    article = get_object_or_404(Article, pk=article_pk)
    serializer = ArticleSerializer(article)
    return Response(serializer.data)

 

Article.objects.get() 대신에 django.shortcuts get_object_or_404 가져왔다.

없으면 에러를 발생시키는 것이 아니고, 없다고 알려주게 된다. (404 페이지 출력)

 

from django.shortcuts import get_object_or_404, get_list_or_404

@api_view(['GET'])

def article_list(request):
    articles = get_list_or_404(Article)
    serializer = ArticleSerializer(articles, many=True)
    return Response(serializer.data)

article_list에서 all()으로 여러 데이터 가져왔던 것도…. get_list_or_404 있다.

 

 

 

@api_view(['GET', 'DELETE', 'PUT'])
def article_detail(request, article_pk):
    article = get_object_or_404(Article, pk=article_pk)
    #디테일 화면
    if request.method == 'GET':
        serializer = ArticleSerializer(article)
        return Response(serializer.data)
    
    #삭제
    elif request.method == 'DELETE':
        pass

    #업데이트
    elif request.method == 'PUT':
        pass

GET, DELETE, PUT으로 나누어준다.

위에서 만들었던 것은 GET으로 넣어준다.

DELETE 삭제, PUT으로 업데이트 동작을 것이다.

 

 

@api_view(['GET', 'DELETE', 'PUT'])
def article_detail(request, article_pk):
    article = get_object_or_404(Article, pk=article_pk)
    if request.method == 'GET':
        serializer = ArticleSerializer(article)
        return Response(serializer.data)
    
    elif request.method == 'DELETE':
        article.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

    elif request.method == 'PUT':
        serializer = ArticleSerializer(article, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)

PUT 메서드 : 수정

ArticleSerializer article, data=request.data 순으로 인스턴스와 데이터를 보내준다.

 

        form = ArticleForm(request.POST, instance=article)

으로 했던 것과 순서가 다르다..

 

 

update에서 serializer valid 하지 않으면, 오류가 발생한다.

is_valid 통과하지 못하면 return 못받았기 때문이다. return None으로 response 아니기 때문에 오류발생.,

  elif request.method == 'PUT':
        serializer = ArticleSerializer(article, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        else:
            return Response(status=status.HTTP_400_BAD_REQUEST)

else: 주면 에러 대신 400 반환하게 한다.

 

    elif request.method == 'PUT':
        serializer = ArticleSerializer(article, data=request.data)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data)

 

is_valid raise_exception 처리를 해주면 위와 같은 처리를 하지 않아도 valid 하지 않을 오류 대신에 400 나타나게 된다.

어디에서 valid 하지 않았는지도 알수있다.

raise_exception = True 하면 오류 내용을 가지고 response 한다.

디테일, 삭제, 수정을 모두

    path('articles/<int:article_pk>/', views.article_detail),

곳에서 메서드만 나누어 처리한 것이다.

 

 

글작성은 article.pk 없는 상태기 때문에

    path('articles/', views.article_list),

사용하면 같다.

 

 

@api_view(['GET', 'POST'])
def article_list(request):
    if request.method == 'GET':
        articles = get_list_or_404(Article)
        serializer = ArticleSerializer(articles, many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = ArticleSerializer(data=request.data)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)

 

위에서 만들었던 article_list 메서드를 GET, POST 나누어 POST 작성으로 하면 된다.

 

 

1:N REST FRAMEWORK

articles - models.py

class Comment(models.Model):
    article = models.ForeignKey(Article, on_delete=models.CASCADE)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

 

comment 모델추가한다.

 

articles - urls.py

urlpatterns = [
    path('comments/', views.comment_list),
    path('comments/<int:comment_pk>/', views.comment_detail),
    path('articles/<int:article_pk>/comments/', views.comment_create),
]

comment 관련 url 추가

1. 전체 댓글 가져오기 2. 댓글 id - > 댓글 3. 댓글 작성

 

articles - views.py

@api_view(['GET'])
def comment_list(request):
    if request.method == 'GET':
        # comments = Comment.objects.all()
        comments = get_list_or_404(Comment)
        serializer = CommentSerializer(comments, many=True)
        return Response(serializer.data)

 

전체 댓글조회

 

serializers.py

class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Comment
        fields = '__all__'
        read_only_fields = ('article',)

 

comment serializer 생성한다.

comment 참조한다. (model)

client 부터 데이터가 제대로 입력되었는지 확인하고, DB 저장하고, model 인스턴스를 json으로 변경하는 역할을 한다.

client 댓글을 write , article 클라이언트가 입력하는 값이 아니다. 댓글 정보를 가지고 때는 article 정보도 필요하다.

read_only_fields 속성으로 읽을 때만 가져오도록 한다. 때는 article 제외한다.

 

 

@api_view(['POST'])
def comment_create(request, article_pk):
    article = get_object_or_404(Article, pk=article_pk)
    serializer = CommentSerializer(data=request.data)
    if serializer.is_valid(raise_exception=True):
        serializer.save(article=article)
        return Response(serializer.data, status=status.HTTP_201_CREATED)

특정 게시글에 댓글 달기 (create)

article_pk article 가져오고

request.data 데이터를 넣어 CommentSerializer 객체 생성한다. is_valid()하면 저장한다. valid 하지 않으면 오류를 보여주도록 raise_exception=True설정한다. 모델 폼에서는 commit=False 하고, 저장되지 않고 생성한 인스턴스에 article 넣어주었다. serializer에서는 저장할 바로 article = article 저장할 있다.

 

 

게시글 게시글에 달린 댓글도 함께 조회

게시글:댓글 - 1:N

comment 모델은 article 외래키로 가지고 있어서 바로 참조한다.

article 게시글과 연관된 댓글을 조회하려면 역참조해야한다. 역참조 매니저 : comment_set

comment FK related_name = 'comments' 역참조 매니저 이름을 바꿀수도 있다.

게시글과 게시글에 달린 댓글까지 모두 가져오고 싶으면 게시글을 serialize , 나를 참조하고 있는댓글도 함께 serialize 하면 된다.

모델 생성, 역참조시 생성되는 comment_set override(덮어씌우기) 하면 된다.

 

serializers.py

class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Comment
        fields = '__all__'
        read_only_fields = ('article',)

 

CommentSerializer 사용할 있도록 articleserializer 위로 올린다.

 

 

class ArticleSerializer(serializers.ModelSerializer):
    comment_set = CommentSerializer(many=True, read_only=True)
    comment_count = serializers.IntegerField(source='comment_set.count', read_only=True)
    class Meta:
        model = Article
        fields = '__all__'

 

comment_set 재정의해준다.

여러 comment 가져오기 때문에 many=True 해준다.

게시글이 작성될때 (데이터 생성될때 ) 댓글 데이터는 있을 없다. 그래서 read_only = True해준다. True해주지 않으면 게시글을 작성할 댓글이 있어야되는 상태가 된다.

이제 article 조회하면, 댓글 정보도 조회된다. (key : comment_set)

 serializers.IntegerField()

댓글 개수 출력하기 위해 comment_count라는 속성도 만들었다. source comment_set.count 가져온다. 이것도 read_only이다.

 

path('comments/<int:comment_pk>/', views.comment_detail),

@api_view(['GET', 'DELETE', 'PUT'])
def comment_detail(request, comment_pk):
    comment = get_object_or_404(Comment, pk=comment_pk)

    if request.method == 'GET':
        serializer = CommentSerializer(comment)
        return Response(serializer.data)

    elif request.method == 'DELETE':
        comment.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

    elif request.method == 'PUT':
        serializer = CommentSerializer(comment, data=request.data)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data)

 

 

 

 

 

 

 

 

 

 

 

 

'Back-end > Django' 카테고리의 다른 글

Django 배포 - EC2, gunicorn, nginx  (0) 2023.01.18
REST API (N:M)  (0) 2022.11.15
REST API, 1:N, M:N - movie, actor, review  (0) 2022.10.21
Fixtures - dumpdata, loaddata  (0) 2022.10.12
Django M:N 관계 - User끼리의 follow 구현하기  (0) 2022.10.12