django queryset 여러개인 api 만들기

장윤성·2022년 8월 25일
0
post-thumbnail

개요

저번 포스팅까지는 queryset을 하나 사용하거나 아니면 model하나를 이용해서 database에 있는 데이터를 가져오는 것을 알아봤는데, 이번에는 특정한 keyword를 가지고 있는 데이터를 들고오는 api와 foreign key로 관계되어 있는 다른 model을 가져와서 새로운 class를 return해주는 api를 만들어 볼 예정이다.

models.py

이번에는 사용자의 특성을 알수 있는 카드에 대한 정보를 담은 Card라는 model을 만들었다.

# card/models.py
from django.db import models

class Card(models.Model)
	id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=100)
    category = models.IntegerField(null=False)
    description = models.TextField(null=False)

serializers.py

마찬가지로 serializer를 작성해서 type casting이 편하도록 했다.

from rest_framework import serializers
from .models import Card

class CardSerializer(serializers.ModelSerializer):
    class Meta:
        model = Card
        fields = '__all__'

views.py

일단 keyword를 받아야하기 때문에, keyword를 받기 위한 query를 사용하기로 했다. /card?word="" 이런 식으로 url을 만들었기 때문에 word에 있는 text를 받기 위한 코드가 필요했다.

keyword = request.GET.get('word',None)

이런식으로 하면 word가 query에 들어와있지 않은 경우에 None을 출력하고 있는 경우에는 '='뒤에 있는 text를 keyword변수에 넣어준다.

keyword가 포함되는 데이터 찾기

keyword를 담았으니 이제 그 단어가 포함되는 데이터를 찾아야 하는데, sql에서는 ...like %text% 를 사용해서 찾았지만 django에서는 __contains를 사용해서 간단하게 나타낼 수 있다.

queryset=Card.objects.filter(description__contains=keyword)

나는 카드의 세부정보에 특정한 단어가 있는지 확인 하는 코드를 작성했기 때문에, 바꾸고 싶을 때,__contains앞에를 바꾸어 주면 된다.

전체 코드

def getKeyword(request):
    keyword = request.GET.get('word',None)
    queryset=Card.objects.filter(description__contains=keyword)
    CardSerializer(queryset,many=True)
    return Response(serializer.data)

여러 model을 이용해서 api 작성하기

이번에는 foreign key(외부키)로 연결된 두 model을 가져와서 새로운 class를 만들어서 return 해주는 api를 만들어볼 예정이다.

models.py

일단 User model을 만든 다음 User가 가지고 있는 카드를 담을 수 있는 CardList model을 만들어서 User가 가지고 있는 카드정보를 저장할 수 있는 model을 만들었다. 마지막으로 UserData는 api에 최종적으로 보낼 User와 가지고 있는 카드의 정보를 모두 보낼 수 있도록 할 예정이다.

class User(models.Model):
    id=models.AutoField(null=False,primary_key=True)
    username=models.CharField(null=False,max_length=20,unique=True)
   
class CardList(models.Model):
    id=models.AutoField(primary_key=True)
    user_id=models.ForeignKey('User',on_delete=models.CASCADE)
    card_id=models.IntegerField(null=False)
    
 class UserData:
    def __init__(self,User,array):
        self.User = User
        self.CardList = array

serializer.py

cardListSerializer에서는 가지고 있는 카드의 id는 필요하지 않기 때문에 그것을 제외한 것들만 fields에 담았다.

class userSerializer(serializers.ModelSerializer):  
    class Meta():
        model=User
        fields = '__all__'
        
class cardListSerializer(serializers.ModelSerializer):
    class Meta():
        model=CardList
        fields=("user_id","card_id")

views.py

일단 User의 정보이기 때문에 username을 parameter를 받아서 거기에 대한 데이터를 가져올 예정이다. 그러기 위해서는 아래처럼 코드를 작성해서 찾는다. 단, 이름이 unique하다는 전제하에get이 사용가능하다.

queryset = User.objects.get(username=username)

하지만 username이 없는 경우에는 에러를 발생하기 때문에 거기에 맞는 try/exception문을 작성해줘야 한다.

try:
    queryset = User.objects.get(username=username)
except:
    queryset=None

이대로만 하면 여전히 에러가 발생하기 때문에 return 하는 Response를 수정해줘야한다.

if queryset is None:
        return Response([])
    else:
        ...
        return Response(userData.__dict__)

이제 username에 맞는 데이터를 들고왔으니 거기에 있는 id를 통해서 user가 가지고 있는 card data를 들고 와보자

serializer=userSerializer(queryset)
try:
    cardQueryset = CardList.objects.filter(user_id=serializer.data['id'])
except CardList.DoesNotExist:
    cardQueryset=None
if cardQueryset is None:
    userData = UserData(serializer.data,[])
else:
    ...

가지고 있는 card가 없는 경우도 있기 때문에 예외처리를 같이 진행해줬다. 카드가 없는 경우에는 빈배열을 담아서 보내주기로 했다.

그리고 cardQueryset에는 card에 대한 id만 저장되어 있기 때문에 id를 통해서 card에 대한 정보를 담아서 보내줘야한다. 그 이후에 UserData class에 user에 대한 데이터와 user가 가지고 있는 card에 대한 데이터를 합쳐서 보내주면 된다.

userDataSerializer = cardListSerializer(cardQueryset,many=True)
cardList = []
for content in userDataSerializer.data:
    card=Card.objects.get(id=content['id'])
    cardList.append(cardSerializer(card))
userData = UserData(serializer.data,cardList)

단, api에 보낼 수 있는 형식은 json이기 때문에 class를 dictionary로 바꾸어서 보내줘야 한다.

return Response(userData.__dict__)

전체코드

@api_view(['GET'])
def getUserInfoByName(self,username):
    try:
        queryset = User.objects.get(username=username)
    except:
        queryset=None
    if queryset is None:
        return Response([])
    else:
        serializer=userSerializer(queryset)
        try:
            cardQueryset = CardList.objects.filter(user_id=serializer.data['id'])
        except CardList.DoesNotExist:
            cardQueryset=None
        if cardQueryset is None:
            userData = UserData(serializer.data,[])
        else:
            userDataSerializer = cardListSerializer(cardQueryset,many=True)
            cardList = []
            for content in userDataSerializer.data:
                card=Card.objects.get(id=content['id'])
                cardList.append(cardSerializer(card))
            userData = UserData(serializer.data,cardList)
        return Response(userData.__dict__)

후기

아마 for문을 사용할 뿐만 아니라 거기에 따라서 호출을 여러번 하기 때문에 속도가 늦어지는 단점이 있는데, 이 과정은 aiohttp를 이용한 비동기 처리를 해주면 훨씬 더 빠른 속도의 api를 만들 수 있다.

profile
소개를 어떻게 한줄로 해요..

0개의 댓글