TIL(Today I Learned)

6월 27일 TIL - 전체 검색

Hyerin P. 2023. 6. 27. 20:28

▷ 오늘의 배움

📖 참조하는 모델의 필드로 필터링 할 때

Model.objects.filter(관련된모델__필드__contains="query")

 

✏️ 한가지 항목에서 여러개를 필터링 할 때!

# query = request.GET.getlist()을 사용하여 리스트 형식으로 받아올 수 있다.
Model.objects.filter(field__in=[a,b])

✏️ 딕셔너리를 활용해서 필터링하기

class APIView(View):
    def get(self, request):
    # 쿼리에 따라 필터링할 항목을 만든다.
        filter_categories = {
            'query'       : 'field__in',
            'query2'       : 'field2__in',
            ...
        }
        # request에서 필터링 값을 가져온다음 filter_categories에 있으면 키값을 이용하여 필터링할 필드와 값으로 이루어진 딕셔너리를 만든다.
        filter_set = {
            filter_categories.get(key) : value for (key, value) in dict(request.GET).items() if filter_categories.get(key)
        }
        # 필터 조건이 딕셔너리 형태이기 때문에 **언패킹을 해주어야한다.
        products = Product.objects.filter(**filter_set).distinct()

▷ 오늘의 시도

💬 전체 검색을 해보자!

처음에 검색 기능을 구현했을 때 전체 검색 없이 게시글, 태그, 장소에 대한 검색필터만 제공 했었다. 하지만 전체 검색도 필요하다 느꼈고 그를 구현하고자 하였다. 처음에는 if, elif 문으로 옵션을 구분해서 각각의 옵션에 따라 필터링을 하였다. 그래서 elif 문을 추가하여 전체 검색일 때 다시한번 필터링 코드를 작성해야하나 막막했다. 분명 뭔가 더 간단하고 좋은 방법이 있을 것 같았는데 말이다. 그래서 이리저리 살펴보던 중 전체 검색일 때 각각 옵션을 나눠줬던것을 다 한번씩 돌린다음에 중복제거를 하면 되지 않을까??? 싶은 생각이 들었다. 그래서 각각의 옵션들을 모두 if 문으로 설정해주고 조건으로 옵션이 'all'일 경우를 하나 더 주었다. 그러면 전체 검색을 할 때 각각의 필터들이 돌아가면서 게시글이 반환될테니까 말이다. 그리고 마지막에 중복된 게시글이 있을 수도 있기에 중복 제거도 한번 해주고 쿼리셋을 반환하는 방향으로 구현을 해보았다.

def get_queryset(self):
        search = self.request.query_params.get("search")
        option = self.request.query_params.get("option")
        queryset_list = []
        # 글 검색
        if option == "article" or option == "all":
            queryset = Article.objects.filter(db_status=1)
            if search is not None:
                queryset = (
                    queryset.filter(
                        Q(title__icontains=search) | Q(content__icontains=search)
                    )
                    .distinct()
                    .order_by("-created_at")
                )
                for a in queryset:
                    queryset_list.append(a)
        # 태그 검색
        if option == "tag" or option == "all":
            queryset = Tag.objects.filter(Q(db_status=1) & Q(tag__icontains=search))
            if search is not None:
                for a in queryset:
                    taglist = a.taglist_set.all().order_by("-created_at")
                    for b in taglist:
                        if b.article.db_status == 1:
                            queryset_list.append(b.article)
        # 지역 검색
        if option == "location" or option == "all":
            search_list = search.split(" ")
            location_list = []
            queryset = MapDataBase.objects.filter(db_status=1)
            if search is not None:
                for location in search_list:
                    queryset = (
                        queryset.filter(
                            Q(jibun_address__icontains=location)
                            | Q(road_address__icontains=location)
                        )
                        .distinct()
                        .order_by("-created_at")
                    )
                    for loc in queryset:
                        location_list.append(loc)
                for a in list(dict.fromkeys(location_list)):
                    article_list = a.article_set.filter(db_status=1).order_by(
                        "-created_at"
                    )
                    for b in article_list:
                        queryset_list.append(b)
        # 중복 검색 값 제거
        result = []
        for i in queryset_list:
            if i not in result:
                result.append(i)
        return result

그리고 하나하나 역참조를 하면서 검색어를 찾았는데 오늘의 배움에 있는 방법을 활용하면 좀 더 코드가 간결하고 깔끔해 질 수 있을 것 같다. 여유가 될 때 한번 코드를 다시금 짜봐야겠다.