2023-06-14 TIL

0v0baek·2023년 6월 14일
0

TIL

목록 보기
73/92

[django] QuizGenerator 범용적으로 사용 가능하게 refactorying

📃 기존 코드

class QuizGenerator:
    def generate():
        quiz_objects_ids = Quiz.objects.values_list("id", flat=True)
        id_list = list(quiz_objects_ids)

        generate_count = 10

        quiz_ids = random.sample(id_list, k=generate_count)

        return Quiz.objects.filter(id__in=quiz_ids)

기존에 팀원분께서 만들어주신 코드는
특정 모델에만 적용이 가능하게 되어있다.

이 코드를 다른 모델이 생겼을 때도 사용 가능하게 바꿔보면 어떨까?

🔧 이론을 기반으로 코드 리팩토링

generators.py

# crawled_data/generators.py

from crawled_data.models import NaverQuiz, KrDictQuiz
import random

class QuizGenerator:
    def generate(model):
        quiz_objects_ids = model.objects.values_list("id", flat=True)
        id_list = list(quiz_objects_ids)

        generate_count = 10

        quiz_ids = random.sample(id_list, k=generate_count)

        return model.objects.filter(id__in=quiz_ids)

함수에서 model이라는 인자를 받아오게 고친 뒤,
기존 모델이 들어가는 부분을 전부 model로 고쳤다.

그리고, view에서 호출할 때 모델 값을 담아서 보내주면 될 것 같았다!

❓ 시리얼 라이저 고민

1번 안

# crawled_data/serializers.py

...

def generate_quiz_serializer(model):
    class GeneratedQuizSerializer(serializers.ModelSerializer):
        if model == NaverQuiz:
            options = NaverOptionSerializer(many=True)

            class Meta:
                model = NaverQuiz
                fields = "__all__"

        elif model == KrDictQuiz:
            explain = KrDictExampleSerializer(many=True)

            class Meta:
                model = KrDictQuiz
                fields = "__all__"

    return GeneratedQuizSerializer

이왕이면 시리얼라이저도 하나로 바꿔주고 싶어서 함수 하나로 묶어봤는데
분리하는 게 더 나을 것 같기도 하고....

2번 안 (분리)

# crawled_data/serializers.py

...

class NaverQuizSerializer(serializers.ModelSerializer):
    options = NaverOptionSerializer(many=True)

    class Meta:
        model = NaverQuiz
        fields = "__all__"


class KrDictQuizSerializer(serializers.ModelSerializer):
    explain = KrDictExampleSerializer(many=True)
    example = KrDictExampleSerializer(many=True)

    class Meta:
        model = KrDictQuiz
        fields = "__all__"

음!!
아무리 생각해도 2번이 훨씬 나을 것 같다.
무조건 메소드를 통일하는 게 정답은 아닌듯 함...

views.py

from rest_framework import status
from rest_framework.decorators import APIView
from rest_framework.response import Response

from crawled_data.models import NaverQuiz, KrDictQuiz
from crawled_data.generators import QuizGenerator
from crawled_data.serializers import NaverQuizSerializer, KrDictQuizSerializer


class NaverQuizView(APIView):
    def get(self, request):
        quiz = QuizGenerator.generate(NaverQuiz)
        serializer = NaverQuizSerializer(quiz, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)


class KrDictQuizView(APIView):
    def get(self, request):
        quiz = QuizGenerator.generate(KrDictQuiz)
        serializer = KrDictQuizSerializer(quiz, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

각 모델에 따라서 퀴즈를 불러와서 보내는 generator를 작성했다.
이건 메소드를 합칠 수 있을 것 같은데...
작동 잘 되는지 확인해보고 디벨롭 해봐야겠다.

urls.py

from django.urls import path
from crawled_data import views

urlpatterns = [
    path("naver/", views.NaverQuizView.as_view(), name="naver_quiz_view"),
    path("krdict/", views.KrDictQuizView.as_view(), name="krdict_quiz_view"),
]

🚫 1차 작동 확인 - 오류 발생

역시나! 한번에 될 리가 없음.

문제가 뭐였나 보니 함수 호출 시 안에 넣어주는 값 문제였다.

# crawled_data/views.py

...

class NaverQuizView(APIView):
    def get(self, request):
        quiz = QuizGenerator.generate(NaverQuiz)
        serializer = NaverQuizSerializer(quiz, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)
        
...

generate 함수를 호출 할 때 NaverQuiz 를 넣어주면 될 줄 알았다?
근데 안됨...ㅋ

형태가 다르게 넘어간다.

이렇게 class 객체로 넘어가서 실제로 generate 함수에서는 아래처럼 들어가는거다.

# crawled_data/generators.py

...

class QuizGenerator:
    def generate(model):
        quiz_objects_ids = <class 'crawled_data.models.NaverQuiz'>.objects.values_list("id", flat=True)
        id_list = list(quiz_objects_ids)

이러면 되겠냐고!!! 당연히 못찾지...

그래서 형태 자체는 문자열로 넘겨줘서 generate 함수 안에서 model을 찾아오는 식으로 바꿔야 할 것 같다.

🔧 1차 작동 오류를 기반으로 리팩토링

# crawled_data/generators.py

...

my_model = {"n": NaverQuiz, "k": KrDictQuiz}


class QuizGenerator:
    def generate(key):
        quiz_objects_ids = my_model[key].objects.values_list("id", flat=True)
        id_list = list(quiz_objects_ids)

        generate_count = min(10, len(id_list))

        quiz_ids = random.sample(id_list, k=generate_count)

        return my_model[key].objects.filter(id__in=quiz_ids)

딕셔너리로 해서 들고오면 안되나? 라는 생각이 들었다.
근데 이것도 안되더라...

💡 진짜 문제점 - DB 데이터 load 안함

하...................................................................................................................

나는 바보다...
DB에 loaddata 안한거 까먹고 아니 왜 안불러와져!! 이러고 있었는데
당연히 안되지 이자식아...............................................................

ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

그니까 사실 첫번째에 짰던 generator 코드 자체가 문제는 아니었던거다..................................................................

이거 기반으로 다시 고쳐야겠다.

🚫 (진짜) 1차 작동 확인 - 오류 발생

진짜로 데이터 있는 DB를 들고와서 실험해봤다.

AttributeError: Got AttributeError when attempting to get a value for field `explain` on serializer `KrDictQuizSerializer`.     
The serializer field might be named incorrectly and not match any attribute or key on the `KrDictQuiz` instance.
Original exception text was: 'KrDictQuiz' object has no attribute 'explain'.

오류가 뜬다.
그래도 아까같은 오류가 아니라 해결의 기미가 보인다 !!!

🔧 (진짜) 1차 오류 해결

아... 이것도 너무 어이없음
진짜 휴먼에러임

# crawled_data/serializers.py

...

class KrDictQuizSerializer(serializers.ModelSerializer):
    explains = KrDictExplainSerializer(many=True)
    examples = KrDictExampleSerializer(many=True)

...

KrDictExplainSerializer으로 써야되는데 저걸 Example로 써놨더라..
글고 related_nameexplains인데 explain하고 써놔서 안됐던거임

휴먼에러 레전드다 진짜...
이제 걍 화나지도 않고 웃김

✅ (진짜) 2차 작동 확인 - 성공

하..
됐다............
ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ

진짜 감사합니다 하느님부처님알라신님어쩌고무튼종교의신님그리고코딩의신님...

profile
개발 공부 하는 비전공자 새내기. 꾸준히 합시다!

0개의 댓글