[ISSUE] 2종 데이터 받아오는 api를 restful하게 변경 작업
- -
[22.11.29] 2종류 데이터를 받아오는 api, restful하게 변경 작업
🍃 many-to-many serialize
✔️ many-to-many 관계가 존재하는 Model Site, Tag의 데이터를 하나의 api를 요청해서 동시에 받아오도록 serialize와 models, view쪽 작업을 진행했었다.
serializers.py
**class TagSerializer(ModelSerializer):
name = serializers.CharField(max_length=20, allow_blank=False, trim_whitespace=True)
class Meta:
model = Tag
fields = ['id', 'name']**
class SiteSerializer(ModelSerializer):
CATEGORY_CHOICES = [(1, 'python'), (2, 'django'), (3, 'javascript'), (4, 'orm'), (5, 'mysql'), (6, 'drf'), (7, 'docker'), (8, 'os'), (9, 'aws'), (10, 'html'), (11, 'css'), (12, 'git'), (13, 'linux')]
title = serializers.CharField(max_length=100, allow_blank=False, trim_whitespace=True)
thumbnail_url = serializers.URLField(max_length=200, min_length=None, allow_blank=False)
host_name = serializers.CharField(max_length=30, allow_blank=False, trim_whitespace=True)
content = serializers.CharField(max_length=2000, allow_blank=True)
category = serializers.ChoiceField(choices=CATEGORY_CHOICES)
user = serializers.CharField(max_length=10, allow_blank=False, trim_whitespace=True)
favorite = serializers.BooleanField(default=False)
video = serializers.BooleanField(default=False)
video = serializers.BooleanField(default=False)
**tag = TagSerializer(many=True, read_only=True)**
def create(self, validated_data):
return Site.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.category = validated_data.get('category', instance.category)
instance.favorite = validated_data.get('favorite', instance.favorite)
instance.video = validated_data.get('video' , instance.video)
instance.save()
return instance
class Meta:
model = Site
fields = ['id','title', 'thumbnail_url', 'host_name'
, 'content', 'category', 'user', 'favorite', 'video'**, 'tag'**]
✔️ serializer에 TagSerializer를 작성한 후, SiteSerializer를 거칠때 반환 값을 Tag도 포함 되게 받을 수 있게 tag라는 칼럼을 설정해 주고 Meta-fields에 tag를 추가해 주었다. serializer에서 이렇게 태그를 사용하기 위해 Model에도 설정을 잡아주어야 했다.
Models.py
class Tag(models.Model):
""" 웹 항목 태그 목록 모델 """
name = models.CharField(verbose_name='이름', max_length=20)
site = models.ManyToManyField(Site, **related_name='tag'**,verbose_name='리스트')
def __str__(self):
return f"{self.name}"
class Meta:
verbose_name = '태그'
verbose_name_plural = '태그 목록'
✔️ 기존에는 site칼럼에 related_name옵션으로 tag가 설정되어 있지 않았지만 시리얼라이즈에서 호출해서 사용하기 위해 추가해주었다.
Views.py > TagsAPIView
class TagsAPIView(APIView):
"""
Site model에 Tag model값이 존재하는 것만 조회
"""
def get(self, request):
word: str = request.GET['word']
tags: list = [tag for tag in Tag.objects.all()]
list_qs = Site.objects.filter(
(Q(tag__in=tags))&(
Q(title__contains=word)|
Q(host_name__contains=word))).order_by('created_at').distinct()
serializer = SiteSerializer(list_qs, many=True)
return Response(serializer.data)
✔️ 태그의 모든 값을 불러와서 사이트를 조회할 때 태그가 포함되어 있는 사이트만 조회 될 수 있게 filter 조건으로 추가해 준 후 serialize를 통해 위에서 작성해둔 tag데이터를 반환할 수 있게 작성하였다.
📌 Issue
🍃 해결방법
✔️ 먼저 위와 같은 상황을 해결하기 위해 1개의 api를 분리하여 2개의 api요청을 주어 값을 변경할 수 있게 코드를 수정해야 했다.
📌 수정 전
get-site-list.js
//${apiURL[apiUrlKey]} = /api/tags/
async function getSiteList(word='') {
/* 각 탭에 해당되는 모든 항목들을 조회하여 함수 */
// 선택한 탭 활성화하기
makeActive()
// 해당되는 항목들 조회하기
await fetch(`${apiURL[apiUrlKey]}?word=${word}`)
.then(response => response.json())
.then(data => {
mapPosts(data)
})
.catch(err => {
console.log(err);
})
}
✔️ 위와 같이 fetch를 통해 api를 통신을 통해 값을 불러오는데 위와 같은 작업으로 data안에 site의 데이터와 tag의 데이터가 모두 존재하고 있다.
function mapTags(data) {
/* 태그 화면 구현 함수 */
// root 모든 요소 초기화
removeAllNode(root)
// 사이트 조회하는 함수
data.map(item => {
renderItem(item);
})
// 태그 조회하는 함수
renderTag(data);
}
✔️ 전달 받은 데이터를 site를 조회하는 함수와, tag를 조회하는 함수에 각각 보내줘 데이터를 조회 할 수 있게 작성하였는 데 위의 fetch와 바인딩 되는 구조를 수정해 보겠다.
📌 수정 후
✔️ 먼저 모든 태그 화면을 클릭하여 들어 갔을 때 위에서 작성한 getSiteList()함수가 호출되게 작성되어있었다.
✔️ getSiteList()함수는 fetch가 한번만 호출되는 구조를 가졌기 때문에 태그화면은 다른 함수를 호출할 수 있게 분기를 걸어주었다.
// '모든 태그'는 site, tag관련하여 api를 2번 호출하기 위해 분기
apiUrlKey === 'tags' ? getSiteByTagList() : getSiteList()
✔️ 모든 태그 화면으로 이동하였을 때 apiUrlKey는 tags라는 값을 가지고 있는데 그 값을 가지면 tag화면을 조회하는 getSiteByTagList()를 호출하게 하였다.
get-site-list.js
function getSiteByTagList(word='') {
/* 모든 태그 화면을 조회하기 위한 함수 */
// 선택 메뉴 활성화
makeActive()
const siteFetch = fetch(`/api/sites?word=${word}`).then(response => response.json())
const tagFetch = fetch(`/api/tags`).then(response => response.json())
Promise.all([siteFetch, tagFetch])
.then(result => {
let siteData = result[0]
let tagData = result[1]
mapPosts(siteData)
mapTags(tagData)
})
.catch(error => console.log(error))
}
참고사이트
[Javascript] fetch 사용법 비동기식 프로그래밍 이해하기
Promise.all() - JavaScript | MDN
serializers.py
class TagSerializer(ModelSerializer):
name = serializers.CharField(max_length=20, allow_blank=False, trim_whitespace=True)
class Meta:
model = Tag
fields = ['id', 'name']
class SiteSerializer(ModelSerializer):
CATEGORY_CHOICES = [(1, 'python'), (2, 'django'), (3, 'javascript'), (4, 'orm'), (5, 'mysql'), (6, 'drf'), (7, 'docker'), (8, 'os'), (9, 'aws'), (10, 'html'), (11, 'css'), (12, 'git'), (13, 'linux')]
title = serializers.CharField(max_length=100, allow_blank=False, trim_whitespace=True)
thumbnail_url = serializers.URLField(max_length=200, min_length=None, allow_blank=False)
host_name = serializers.CharField(max_length=30, allow_blank=False, trim_whitespace=True)
content = serializers.CharField(max_length=2000, allow_blank=True)
category = serializers.ChoiceField(choices=CATEGORY_CHOICES)
user = serializers.CharField(max_length=10, allow_blank=False, trim_whitespace=True)
favorite = serializers.BooleanField(default=False)
video = serializers.BooleanField(default=False)
video = serializers.BooleanField(default=False)
// tag = TagSerializer(many=True, read_only=True)
def create(self, validated_data):
return Site.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.category = validated_data.get('category', instance.category)
instance.favorite = validated_data.get('favorite', instance.favorite)
instance.video = validated_data.get('video' , instance.video)
instance.save()
return instance
class Meta:
model = Site
fields = ['id','title', 'thumbnail_url', 'host_name', 'content'
, 'category', 'user', 'favorite', 'video']
✔️ 현재 사이트 조회 시 tag데이터는 필요하지 않기 때문에 Meta-fields에서 tag항목 제거
✔️ 태그 칼럼은 사용하지 않기 때문에 필요한 경우 다시 추가해 주겠다.
models.py
class Tag(models.Model):
""" 웹 항목 태그 목록 모델 """
name = models.CharField(verbose_name='이름', max_length=20)
site = models.ManyToManyField(Site, verbose_name='리스트')
def __str__(self):
return f"{self.name}"
class Meta:
verbose_name = '태그'
verbose_name_plural = '태그 목록'
✔️ 추가 되었던 related_name='tag' 제거
views.py > TagsAPIView
class SiteByTagAPIView(APIView):
"""
Site model에 Tag model값이 존재하는 것만 조회
"""
def get(self, request):
word: str = request.GET['word']
tags: list = [tag for tag in Tag.objects.all()]
list_qs = Site.objects.filter(
(Q(tag__in=tags))&(
Q(title__contains=word)|
Q(host_name__contains=word))).order_by('created_at').distinct()
serializer = SiteSerializer(list_qs, many=True)
return Response(serializer.data)
class TagsAPIView(APIView):
"""
Site model에 Tag model값이 존재하는 것만 조회
"""
def get(self, request):
"""
Site 태그 조회
"""
tags = Tag.objects.all()
serializer = TagSerializer(tags, many=True)
return Response(serializer.data)
✔️ 좌측 사이트조회 와 우측 태그를 각각 조회하는 로직으로 변경
'ISSUE > Drf' 카테고리의 다른 글
[ISSUE] APIView로 bulk update 구현 (0) | 2023.05.05 |
---|
소중한 공감 감사합니다