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 |