Django Rest Framework
ForeignKey(이하 Fk) 혹은 ManyToMany로 연결된 상태의 모델들을 Get 방식으로는 중첩된 데이터를 받고, POST 혹은 UPDATE 시에는 PrimaryKey(이하 Pk)로만 보내는 방식에 대해 써보고자 합니다. POST와 UPDATE 때는 참조되는 테이블의 정보를 생성하거나 수정할 수 없습니다.
즉, 이 방식은 이미 있는 참조데이터를 POST, UPDATE할 때 참조위치를 변경하는 정도로 사용됩니다.
글로만 이해가 안되시는 분들을 위해 사진으로 먼저 보여드리고 진행하도록 하겠습니다.
다음과 같이 하는 이유는 이미 정해져있는 B 테이블의 요소를 참조해 Get할 때 B 테이블의 요소들을 보여주고, Post나 Update할 때는 단순히 참조만 해서 이미 정해진 요소들을 사용하도록 하기 위함입니다. 저는 이 부분을 "등급, 그룹, 카테고리" 등을 지정할 때 사용했습니다.
1. Setting !
우선 기본 세팅부터 하도록 하겠습니다.
1) Models.py
from django.db import models
class Category(models.Model):
title = models.CharField("Title", max_length=255)
class Tag(models.Model):
title = models.CharField("Title", max_length=255)
class Post(models.Model):
title = models.CharField("Title", max_length=255)
description = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
tags = models.ManyToManyField(Tag)
Post 모델은 Category 모델을 참조하고 있고, Tag 모델과는 다대다(ManyToMany) 관계에 있습니다.
2) serializer.py
from rest_framework import serializers
from .models import Category, Tag, Post
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = "__all__"
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = "__all__"
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = "__all__"
기본적인 ModelSerializer 입니다.
3) views.py
from rest_framework.viewsets import ModelViewSet
from .models import Post
from .serializer import PostSerializer
class PostViewSet(ModelViewSet):
serializer_class = PostSerializer
queryset = Post.objects.all()
Post 기능에 대한 ModelViewSet입니다.
2. Solution
현재는 Fk와 ManyToMany로 연결되어 있을 뿐입니다. Get을 할 때 각각 Category의 Pk와 Tag의 Pk 리스트를 불러옵니다. Post 할 때도 마찬가지로 Category의 Pk와 Tag의 Pk 리스트를 보내줍니다.
여기서 Get을 할 때 Category와 Tags를 상세하게 볼 수가 없습니다. 이제 이 부분을 상세하게 볼 수 있도록 하겠습니다.
1) NestedSerializerMixin 만들기
# views.py
from rest_framework.viewsets import ModelViewSet
from .models import Post
from .serializers import PostSerializer
+class NestedSerializerMixin(ModelViewSet):
+ read_serializer_class = None
+ def get_serializer_class(self):
+ if self.request.method.lower( ) == 'get':
+ return self.read_serializer_class
+ return self.serializer_class
class PostViewSet(ModelViewSet):
. . .
위와 같은 코드를 views.py 파일의 상단이나 따로 파일을 만들어 import 해줍니다. 저는 views.py 파일 상단에 위치하도록 입력하겠습니다.
위 코드는 serializer 사용 요청이 왔을 때, 만약 'GET' 방식이면 read_serializer_class 를 실행하고, 'POST', 'UPDATE' 등의 요청이면 serializer_class 를 실행합니다. 몇 단계 뒤에 우리는 이 NestedSerializerMixin 을 상속 받아 사용할 예정입니다.
2) NestedPostSerializer 만들기
# serializers.py
class PostSerializer(serializers.ModelSerializer):
. . .
+class NestedPostSerializer(serializers.ModelSerializer):
+ category = CategorySerializer()
+ tags = TagSerializer(many=True)
+ class Meta:
+ model = Post
+ fields = '__all__'
NestedPostSerializer에서는 CategorySerializer( ) 와 TagSerializer(many=True)를 실행시킵니다.
3) read_serializer_class 추가
from rest_framework.viewsets import ModelViewSet
from .models import Post
from .serializers import PostSerializer, +NestedPostSerializer
class NestedSerializerMixin(ModelViewSet):
. . .
-class PostViewSet(ModelViewSet):
+class PostViewSet(NestedSerializerMixin):
serializer_class = PostSerializer
queryset = Post.objects.all()
+ read_serializer_class = NestedPostSerializer
PostViewSet에서 NestedSerializerMixin을 상속받고, get 방식일 때 실행될 NestedPostSerializer를 read_serializer_class에 할당해줍니다.
이제 다시 Get 요청을 보내면 아래와 같은 결과를 받을 수 있습니다. Post 요청은 위에서 했던 방식과 동일합니다.
[
{
"id": 1,
"category": {
"id": 1,
"title": "Category Title 1"
},
"tags": [
{
"id": 1,
"title": "tag title 1"
}
],
"title": "title1",
"description": "description1"
},
{
"id": 2,
"category": {
"id": 2,
"title": "Category Title 2"
},
"tags": [
{
"id": 1,
"title": "tag title 1"
},
{
"id": 2,
"title": "tag title 2"
},
{
"id": 3,
"title": "tag title 3"
}
],
"title": "title2",
"description": "description2"
}
]
우리가 이런식으로 Post하고 Get하는 이유는 Get 할 때 많은 정보를 보내주고, Post 할 때는 최소한의 정보를 받기 위함입니다. 또한 Post 모델에 Post할 때 Category모델과 Tag모델에 영향을 주지 않기 위함입니다. 만약 Post모델을 Update하거나 Post할 때 Category 모델과 Tag 모델을 함께 수정하거나 생성하게 되면 문제가 될 수 있습니다.
포스팅된 글을 읽으시면서 어려운 점이 있으시면 댓글 달아주세요 ! 답변드리도록 하겠습니다.
감사합니다.
참고자료
https://www.neerajbyte.com/post/how-to-get-nested-data-in-django-rest-framework
'BackEnd > DRF' 카테고리의 다른 글
DRF - 소셜 로그인 -2(Naver Login) (0) | 2022.07.06 |
---|---|
DRF - 소셜 로그인 - 1(Naver Login) (0) | 2022.06.30 |
ImageField 사용 방법(Upload Image) (0) | 2022.01.13 |