22.01.05 Django RestFramework와 APIView로 queryset으로 DB에 있는 목록 검색(filtering)하기(장르, 제목 검색)

2022. 1. 5. 15:41작업/Django

프론트 분들과 논의한 페이지 와이어프레임(피그마)

yarn react사용하심 백엔드는 장고 사용

 

유의점

 - 영화제목 + 게시물글 제목 + 장르 2개까지 검색 가능하나 AND기능은 불가능합니다(영화제목이 마블 이면서 장르가 액션인 게시글 보여주지 못함 -> 그러나 어차피 검색을 더 큰범위에서 다 보여주니까 이런 작은 프로젝트에서는 크게 상관은 없다 나중에 추가구현 해보기)

 

DB내용

파일구조 backend/Django 프로젝트

models.py
urls.py
views.py
serializers.py
최종 config/urls.py
#swagger 아래 부분은 일단 무시하기

서버 실행

전체 리스트 /small-theater

small-theater

 

상세페이지 small-theater/2
(small-theater2 로 들어가게 구현할 수도 있음 그러려면 small_theeater/urls.py에서 /뺴면 됨)

장르가 로맨스인 게시물 검색 small-theater?search-genre1=로맨스

장르에 로맨스 또는 액션이 들어간 게시물 모두 검색 small-theater?search-genre1=로맨스&search-genre2=액션

장르에 로맨스 또는 액션이 들어간 영화 + 소극장제목이 마블 인 게시물 모두 검색

small-theater?search-genre1=로맨스&search-genre2=액션&title=마블

소극장 제목만 검색(소극장 제목이 마블인 게시물 검색) small-theater?title=마블

이렇게 제목 끝까지 안쳐도 나온다.

장르 하나만 검색할 때 small-theater?search-genre2=공포로 해도 됩니다.

코드

views.py

from django.http import response, JsonResponse, HttpResponse #안씀
from django.shortcuts import render #안씀
from django.views import generic #안씀
from django.db.models import Q #filter시 | 사용하려고 
from urllib import parse #한글 인코딩
from rest_framework.views import APIView #CBV
from rest_framework import status,generics #200 404 등 , ListAPIView
from rest_framework.response import Response
# from rest_framework.decorators import api_view # @api_view FBV
from .models import SmallTheater
from .serializers import SmallTheaterSerializer
# FBV와 Generic View가 있음
# FBV는 세세하게 코딩/ Generic view는 간편 -> 섞어도 됨

# POST,GET 둘다 요청 
# POST은 뭔가 sideeffect가 있을 때(바뀌거나 넣어줘야 할 때) GET은 응답(데이터)만 주면 될 때

# CBV POST,GET,DELETE,UPDATE 참고사이트 
# https://toughbear.tistory.com/60
# **kwargs https://d-yong.tistory.com/61
# 공식문서 https://www.django-rest-framework.org/tutorial/3-class-based-views/
# 쿼리셋 <-> JSON https://www.delftstack.com/ko/howto/django/django-queryset-to-json/

# static, 템플릿 등 참고사이트 https://iamiet.tistory.com/10?category=928115
# api 요청 이용해 drf <-> 리엑트 연동 https://this-programmer.tistory.com/135
# 쿼리셋 all order_by filter 사용법 https://velog.io/@swhybein/django-queryset
# Create your views here.

# 쿼리셋 filter, ordering 일본개발자 블로그 https://freez2385.github.io/posts/Python-Django-django_restframework2/
# filtering, ordering 블로그2 https://donis-note.medium.com/django-rest-framework-filtering%EA%B3%BC-ordering-4e7d1351205a
# 딕셔너리 .get, .items() 등등 https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=sw4r&logNo=221504133335
# 한글인코드 https://dololak.tistory.com/255


# 1.
# class SmallTheaterList(APIView): # 소극장 전체 보기는 구현완료 
#     def get(self,request,**kwargs): # http://localhost/small-theater?search_genre1=romance&search_genre2=romance&title=마블
#         small_theater_list = SmallTheater.objects.all() #쿼리에 따라 DB에 있는 소극장 목록 가져오기
#         small_theater_serializer = SmallTheaterSerializer(small_theater_list,many=True)
#         return Response(small_theater_serializer.data,status=status.HTTP_200_OK)

# 2.
class SmallTheaterList(APIView): # 소극장 목록 보기
    # http://127.0.0.1:8000/small-theater?search-genre1=드라마&search-genre2=공포&title=마블
    def get(self,request,**kwargs):
        search_genre1 = request.GET.get('search-genre1')
        search_genre2 = request.GET.get('search-genre2')
        search_title =request.GET.get('title')
        for key in request.GET.keys():
            if request.GET.get(key)!=None:
                parse.unquote(request.GET.get(key)) #None이 아니면 한글로 바꿔라
        # 1. genre1,genre2,title 셋 다 None인 경우
        if (search_genre1==None) & (search_genre2==None) & (search_title==None):
            final_queryset = SmallTheater.objects.all().order_by('-published_date') # /small-theater
        # 2. title이 있는 경우(__contains오류 떄문에 얘만 따로)
        elif search_title: 
            queryset = SmallTheater.objects.filter(Q(theater_genre1=search_genre1) | Q(theater_genre2=search_genre1) | Q(theater_genre1=search_genre2) | Q(theater_genre2=search_genre2) | Q(title__contains=search_title)) #단어포함 title__contains = 어쩌구
            final_queryset = queryset.order_by('-published_date') #published_date하면 오름차순
        # 3. genre1,genre2,title 중 하나라도 있는 경우
        else:
            queryset = SmallTheater.objects.filter(Q(theater_genre1=search_genre1) | Q(theater_genre2=search_genre1) | Q(theater_genre1=search_genre2) | Q(theater_genre2=search_genre2) | Q(title=search_title)) #단어포함 title__contains = 어쩌구
            final_queryset = queryset.order_by('-published_date')
        target_theater_serializer = SmallTheaterSerializer(final_queryset, many=True)
        return Response(target_theater_serializer.data, status=status.HTTP_200_OK)

class SmallTheaterDetail(APIView): # 소극장 상세보기
    # http://127.0.0.1:8000/small-theater/3
    def get(self,request,**kwargs): #http://localhost:8000/small-theater/{small_theater.id}
        target_theater_id = kwargs.get('id') # 4 (int)
        queryset = SmallTheater.objects.filter(id=target_theater_id) # get은 하나만 가져옴 not iterable이슈 있음
        target_theater_serializer = SmallTheaterSerializer(queryset, many=True)
        return Response(target_theater_serializer.data, status=status.HTTP_200_OK)

models.py

from django.db import models

# 필터링 및 검색기능 참고사이트 https://fierycoding.tistory.com/67

# Create your models here.
class SmallTheater(models.Model):
    # no = models.AutoField(primary_key=True)
    published_date = models.DateField()
    title = models.CharField(max_length=200, default='')
    theater_owner = models.CharField(max_length=10, null=True) # 우리는 소극장생성은 안하고 가상의 유저들이 있다치고 보여줄 거니까 아마 user랑 연결될 필요 없다고 생각! 단순 Char로 가상의 유저1,유저2,유저3들의 닉네임만 넣어주자
    theater_genre1 = models.CharField(max_length=30, null=True) # 'action' / default=''할까
    theater_genre2 = models.CharField(max_length=30, null=True) # 'romance'
    # 이것도 우리끼리 정의한 호러=1, 로맨스=2, 액션=3 이렇게? 총 1,2,3,4,5,6,7,8 몇 까지 있는 걸까?
    # -> 어차피 소극장 목록에서 액션/로맨스 로만 보여줄거니까 일단 검색 편하게 Char로 
    introduce = models.CharField(max_length=1000, null=True)
    notice = models.CharField(max_length=1000, null=True)

    class Meta:
        # managed = False # 자동 migration
        db_table = 'small_theater'

small_theater/urls.py

from django.conf.urls import url, include
from django.urls import path
from . import views #small_theater/views.py 

from django.conf import settings
from django.conf.urls.static import static

app_name = 'small_theater'

urlpatterns =[
    path('', views.SmallTheaterList.as_view()),
    path('/<int:id>',views.SmallTheaterDetail.as_view()) # http://127.0.0.1:8000/small-theater/4 앞에 슬래시 없애면 small-theater4로 들어감
]

serializers.py

# serializer : 객체처럼 보기힘든 데이터를 JSON이나 XML으로 보기쉽게 데이터 바꿔줌
# REST API : Resource 이름가지고 클라이언트와 서버가 통신하는 방법 만들 때 필수적임

# 주의! 장고 서버와 REST 서버는 따로 운영되어야 한다!!
from rest_framework import serializers
from .models import SmallTheater

# 리엑트연동까지 https://this-programmer.tistory.com/135
# Serializer 자세한 설명 https://brownbears.tistory.com/71
class SmallTheaterSerializer(serializers.ModelSerializer):
    class Meta:
        model = SmallTheater # models.py속 모델
        fields = ('__all__') # 통신할 데이터필드 -> 모든 필드 하려면 '__all__'해도 됨
        # fields = ('id','published_date'...,)