DRF

hey hey·2021년 12월 7일
0

Django

목록 보기
8/9
post-thumbnail

Django-RestFrameWork 이용한 API 구현

dummy 만들기

$ python manage.py seed articles --number=20

serializers.py

from rest_framework import serializers
from .models import Article

class ArticleListSerializer(serializers.ModelSerializer):

    class Meta:
        model = Article
        fields = ('id','title')

shell_plus

In [1]: from articles.serializers import ArticleListSerializer

**기본구조확인**
In [2]: serializer = ArticleListSerializer()
In [3]: serializer      
Out[3]: ArticleListSerializer():
    id = IntegerField(label='ID', read_only=True)
    title = CharField(max_length=100)

**Model Instance 객체 확인**
In [4]: article = Article.objects.get(pk=1)
In [5]: article
Out[5]: <Article: Article object (1)>

In [6]: serializer = ArticleListSerializer(article)
In [7]: serializer
Out[7]: ArticleListSerializer(<Article: Article object (1)>):
    id = IntegerField(label='ID', read_only=True)
    title = CharField(max_length=100)

**QuerySet 객체 전체 확인**
In [8]: articles = Article.objects.all()
In [9]: serializer = ArticleListSerializer(articles,**many=True**)
In [10]: serializer.data
Out[10]: [OrderedDict([('id', 1), ('title', 'Daughter type contain degree he medical.')]), 
OrderedDict([('id', 2), ('title', 'Across try industry bring material everybody.')]),

many=True

QuerySet 등을 직렬화하기 위해서는 serializer를 인스턴스화 할 때,

many = True를 키워드 인자로 전달해야 한다.

1. GET - Article List

urls.py

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

views.py

from django.shortcuts import get_list_or_404, render
from rest_framework.response import Response
from rest_framework.decorators import api_view

from .models import Article
from .serializers import ArticleListSerializer

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

@api_view

  • 기본적으로 GET 매서드만 허용되며 다른 메서드 요청에 대해서는 405 Method Not Allowed로 응답
  • View 함수가 응답해야 하는 http 메서드의 목록을 리스트의 인자로 받음
  • DRF에서는 선택이 아닌 필수로 작성해야 view함수가 정상작동 함

2. GET - Article Detail

  • Article List와 Article Detail을 구분하기 위해 추가 Serializer 정의
  • 모든 필드를 직렬화하기 위해 fields 옵션을 all로 지정

serializers.py

from rest_framework import serializers
from .models import Article

class ArticleListSerializer(serializers.ModelSerializer):

    class Meta:
        model = Article
        fields = '__all__'

urls.py

urlpatterns = [
    path('articles/',views.article_list),
    path('articles/<int:article_pk',views.article_detail),
]

views.py

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

21 번 인스턴스값

21 번 인스턴스값

3. POST - Create Article

  • 201 Created 상태 코드 및 메시지 응답
  • RESTful 구조에 맞게 작성
    1. URI는 자원을 표현
    2. 자원을 조작하는 행위는 HTTP Method
  • article_list 함수로 게시글을 조회하거나 생성하는 행위를 모두 처리 가능

views.py

from rest_framework import status

@api_view(['GET','POST'])
def article_list(request):
    if request.method =='GET':      **이부분은 위와 동일** 
        articles = get_list_or_404(Article)
        serializer = ArticleListSerializer(articles,many=True)
        return Response(serializer.data)

    elif request.method =='POST':   **작성부분**
        serializer = ArticleListSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data,status=status.HTTP_201_CREATED )
        return Response(serializer.errors,status=status.HTTP_400_BAD_REQUEST )

DRF에는 status code를 보다 명확하고 읽기 쉽게 만드는 데 사용할 수 있는 함수를 제공한다

status=status.HTTP_201_CREATED,

   status.HTTP_400_BAD_REQUEST ...

Response(serializer.data,status=201) 와 같이 쓸수도 있지만 DRF는 권장하지 않는다.

title 과 content 적어서 POST 로 제출하기 → 201이면 생성, 400이면 실패

if serializer.is_valid(**raise_exception=True**):
  serializer.save()
  return Response(serializer.data,status=status.HTTP_201_CREATED )

raise_exception= True

를 사용하게 되면 오류가 있는경우 알아서 400을 반환한다.

4. DELETE - Delete Article

  • 204 No content 상태 코드 및 메시지 응답
  • article_detail 함수로 상세 게시글을 조회하거나 삭제하는 행위 모두 처리 가능

views.py

  • 조회페이지에서 추가
@api_view(['GET','DELETE'])
def article_detail(request,article_pk):
    article = get_object_or_404(Article,pk=article_pk)
    if request.method =="GET":
        serializer = ArticleListSerializer(article)
        return Response(serializer.data)
    **elif request.method =="DELETE":
        article.delete()
        data ={
            'delete':f'데이터 {article_pk}번이 삭제되었습니다.'
        }
        return Response(data,status=status.HTTP_204_NO_CONTENT)**

DELETE 버튼이 생겼다

삭제 시 내용이 data 내용으로 바뀐다.

5. PUT - Update Article

  • article_detail 함수를 수정해서 수정까지 가능하다.

views.py

@api_view(['GET','DELETE'**,'PUT'**])
def article_detail(request,article_pk):
    ...
    elif request.method =='PUT':
        # serializer = ArticleListSerializer(instance=article,data=request.data)
        serializer = ArticleListSerializer(**article,data=request.data**)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data)

1:N

모델 data를 직렬화(serialization)하여 JSON으로 변환

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)
  • migration 설정 후 dummy 만들기
makemigrations -> migrate ->
python manage.py seed articles --number=20

1. GET- Comment List

serializers.py

class CommentSerializer(serializers.ModelSerializer):

    class Meta:
        model = Comment
        fields = '__all__'

urls.py

path('comments/',views.comment_list)

views.py

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

2. GET - comment Detail

urls.py

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

views.py

@api_view(['GET'])
def comment_detail(request,comment_pk):
    comment = get_object_or_404(Comment,pk=comment_pk)
    serializer = CommentSerializer(comment)
    return Response(serializer.data)

3. POST - Create Comment

urls.py

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

views.py

@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)
  • .save() 메서드는 특정 Serializer 인스턴스를 저장하는 과정에서 추가적인 데이터를 받을 수 있음

READ Only Field

  • 어떤 게시글에 작성하는 댓글인지에 대한 정보를 form-data로 넘겨주지 않았기 때문에 직렬화하는 과정에서 article 필드가 유효성 검사(is_valid)를 통과하지 못함
  • 이때는 read_only_fields 설정을 통해 직렬화하지 않고 반환 값에만 해당 필드가 포함되도록 설정
class CommentSerializer(serializers.ModelSerializer):

    class Meta:
        model = Comment
        fields = '__all__'
        **read_only_field=('article',)**

4. DELETE & PUT - delete,update Comment

  • Article 생성 로직에서와 마찬가지로 comment_detail 함수가 모두 처리할 수 있도록 작성

views.py

@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()
        data ={
            'delete':f'댓글 {comment_pk}번이 삭제되었습니다.'
        }
        return Response(data,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)

1. 특정 게시글에 작성된 댓글 목록 출력

serializers.py

class ArticleListSerializer(serializers.ModelSerializer):
    **comment_set = serializers.PrimaryKeyRelatedField(many=True,read_only=True)**
    class Meta:
        model = Article
        fields = '__all__'
  • pk를 사용하여 관계된 대상을 나타내는 데 사용할 수 있음
  • 필드가 to many relationships(N)를 나타내는데 사용되는 경우 many=True 속성 필요
  • comment_set 필드 값을 form-data로 받지 않으므로 read_only=True 설정 필요

models.py

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

related_name ⇒ 역참조시 생성되는 comment_set을 override 할 수 있음

Nested relationships

  • 모델 관계상으로 참조된 대상은 참조하는 대상의 표현에 포함되거나 중첩(nested)될 수 없음
  • 이러한 중첩된 관계는 serializers를 필드로 사용하여 표현할 수 있다.
  • 두 클래스의 상하 위치 변경
class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Comment
        fields = '__all__'
        read_only_fields=('article',)

class ArticleListSerializer(serializers.ModelSerializer):
    comment_set = **CommentSerializer**(many=True,read_only=True)
    class Meta:
        model = Article
        fields = '__all__'

2. 특정 게시글에 작성된 댓글 개수 구하기

serializers.py

class ArticleListSerializer(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__'

source

→ 필드를 채우는 데 사용할 속성의 이름

→ .count()는 built-in QuerySet API 중 하나

profile
FE - devp

0개의 댓글