Lina's Toolbox

[Django] TypeError : Cannot filter a query once a slice has been taken. 본문

스파르타 내일 배움 캠프 AI 웹개발 과정/troubleshooting

[Django] TypeError : Cannot filter a query once a slice has been taken.

Woolina 2024. 10. 11. 17:22

 

raise TypeError("Cannot filter a query once a slice has been taken.")
TypeError: Cannot filter a query once a slice has been taken.
[11/Oct/2024 17:14:42] "POST /auth/users/recommendations/ HTTP/1.1" 500 106817

 

이런 에러가 발생했다.

 

이미 슬라이싱이 된 쿼리는 filter를 할수 없다고?

 

def post(self, request, *args, **kwargs):
        # 초기화
        matching_reviewee_id = None

        # 요청에서 필터링 값 가져오기
        riot_tiers = request.data.getlist('riot_tier', [])
        positions = request.data.getlist('positions', [])
        filter_fields = request.data.getlist('filter_fields', [])
        user_preference = request.data.get('user_preference', '')

        # 기본 유저 리스트 가져오기
        users = User.objects.all()

        # 1. 기본 필터링 (티어와 포지션에 따라 필터링)
        if riot_tiers:
            users = users.filter(riot_tier__in=riot_tiers)

        if positions:
            users = users.filter(positions__position_name__in=positions)

        # 2. 평가 필드 필터링
        if filter_fields:
            # 필터링 조건 추가
            filter_conditions = {}
            for field in filter_fields:
                if hasattr(Evaluations, field):
                    filter_conditions[f'evaluations__{field}__gte'] = 1  # 필드 값이 1 이상인 경우

            # 유저 필터링
            users = users.filter(**filter_conditions)

            # 평가 항목이 있는 유저만 필터링 후 정렬
            users = users.filter(evaluations__isnull=False).annotate(
                evaluations_count=models.Count('evaluations')
            ).order_by(*[f'-evaluations__{field}' for field in filter_fields]).distinct()

        # 상위 3명만 선택
        users = users[:3]

        # 리뷰 데이터 가져오기
        all_reviews = Articles.objects.all().values('content', 'reviewee')
        reviews_text = "\n".join([f"Review ID: {review['reviewee']} - {review['content']}" for review in all_reviews])

        # 3. 사용자 입력 텍스트 처리
        if user_preference:
            # OpenAI API를 사용하여 유저의 선호도에 맞는 리뷰 분석
            system_instructions = """
            You are tasked with finding the most relevant review for a user based on their preferences.
            Based on the user's preference, identify the review that best matches the following description: {user_preference}.
            Here are all the reviews:
            {reviews_text}.
            Provide only the matching reviewee's ID or IDs in a comma-separated format (e.g., 1 or 1, 2) without any additional text.
            """

            prompt = system_instructions.format(
                user_preference=user_preference,
                reviews_text=reviews_text
            )

            user_preference_analysis = ask_chatgpt(user_message=prompt, system_instructions="")
            print('user_preference_analysis:', user_preference_analysis)

            # 응답 포맷 확인 및 처리
            try:
                if "Review ID:" in user_preference_analysis:
                    # "Review ID: 1, 2" 형식 처리
                    matching_reviewee_ids = [int(id.strip()) for id in user_preference_analysis.split(":")[1].split(",")]
                else:
                    # "1, 2" 형식 처리
                    matching_reviewee_ids = [int(id.strip()) for id in user_preference_analysis.split(",")]
            except (ValueError, IndexError) as e:
                raise ValueError(f"Unexpected response format from OpenAI: {user_preference_analysis}") from e

            # 유저 필터링
            if matching_reviewee_ids:  # 리스트가 비어있지 않은 경우
                users = users.filter(id__in=matching_reviewee_ids)

        
        # 직렬화하여 응답
        serializer = UserSerializer(users, many=True)
        return render(request, 'users/matching_result.html', {'users': serializer.data})

users = users[:3] 로 슬라이싱을 이미 한 후에,

users = users.filter(id__in=matching_reviewee_ids) 이렇게 다시 필터링을 하는 게 문제였다.

 

그래서 상위 3명을 추출하는 코드를 리턴 직전으로 순서를 바꿔줬다.

    def post(self, request, *args, **kwargs):
        # 초기화
        matching_reviewee_id = None

        # 요청에서 필터링 값 가져오기
        riot_tiers = request.data.getlist('riot_tier', [])
        positions = request.data.getlist('positions', [])
        filter_fields = request.data.getlist('filter_fields', [])
        user_preference = request.data.get('user_preference', '')

        # 기본 유저 리스트 가져오기
        users = User.objects.all()

        # 1. 기본 필터링 (티어와 포지션에 따라 필터링)
        if riot_tiers:
            users = users.filter(riot_tier__in=riot_tiers)

        if positions:
            users = users.filter(positions__position_name__in=positions)

        # 2. 평가 필드 필터링
        if filter_fields:
            # 필터링 조건 추가
            filter_conditions = {}
            for field in filter_fields:
                if hasattr(Evaluations, field):
                    filter_conditions[f'evaluations__{field}__gte'] = 1  # 필드 값이 1 이상인 경우

            # 유저 필터링
            users = users.filter(**filter_conditions)

            # 평가 항목이 있는 유저만 필터링 후 정렬
            users = users.filter(evaluations__isnull=False).annotate(
                evaluations_count=models.Count('evaluations')
            ).order_by(*[f'-evaluations__{field}' for field in filter_fields]).distinct()

        # 상위 3명만 선택
        users = users[:3]

        # 리뷰 데이터 가져오기
        all_reviews = Articles.objects.all().values('content', 'reviewee')
        reviews_text = "\n".join([f"Review ID: {review['reviewee']} - {review['content']}" for review in all_reviews])

        # 3. 사용자 입력 텍스트 처리
        if user_preference:
            # OpenAI API를 사용하여 유저의 선호도에 맞는 리뷰 분석
            system_instructions = """
            You are tasked with finding the most relevant review for a user based on their preferences.
            Based on the user's preference, identify the review that best matches the following description: {user_preference}.
            Here are all the reviews:
            {reviews_text}.
            Provide only the matching reviewee's ID or IDs in a comma-separated format (e.g., 1 or 1, 2) without any additional text.
            """

            prompt = system_instructions.format(
                user_preference=user_preference,
                reviews_text=reviews_text
            )

            user_preference_analysis = ask_chatgpt(user_message=prompt, system_instructions="")
            print('user_preference_analysis:', user_preference_analysis)

            # 응답 포맷 확인 및 처리
            try:
                if "Review ID:" in user_preference_analysis:
                    # "Review ID: 1, 2" 형식 처리
                    matching_reviewee_ids = [int(id.strip()) for id in user_preference_analysis.split(":")[1].split(",")]
                else:
                    # "1, 2" 형식 처리
                    matching_reviewee_ids = [int(id.strip()) for id in user_preference_analysis.split(",")]
            except (ValueError, IndexError) as e:
                raise ValueError(f"Unexpected response format from OpenAI: {user_preference_analysis}") from e

            # 유저 필터링
            if matching_reviewee_ids:  # 리스트가 비어있지 않은 경우
                users = users.filter(id__in=matching_reviewee_ids)

        # 상위 3명만 선택
        users = users[:3]
        
        # 직렬화하여 응답
        serializer = UserSerializer(users, many=True)
        return render(request, 'users/matching_result.html', {'users': serializer.data})

그랬더니 문제없이 잘 작동했다!

 

 

 

이미 슬라이싱된 쿼리는 다시 필터링 할 수 없다!!!

필터링을 먼저 한 후에 슬라이싱을 적용하기!

기억!!