본문 바로가기
관리자

Programming-[Backend]/Django

[TIL] Django prefetch_related, Prefetch 2depth, 모델 필드 정보 조회

728x90
반응형

prefetch_related

prefetch_related 구문으로 1:N 관계에서 1 -> N 으로 접근하여 데이터들을 한 번에 fetch(eager_loading)할 수 있다.

 

Company : Team = 1 : N, Team: Color = 1:1 인 경우라면,

 

아래와 같이 prefetch 구문을 적용하면 된다. Prefetch class는 prefetch_related 구문을 더 상세히 사용할 수 있게 해준다.

  • "team_set" : Company 모델에서 Team 모델을 참조하기 위한 related_name이다. models.py에서 따로 지정하지 않았다면 "{이름}_set"으로 자동으로 설정된다. 이름 찾는법은 아래 모델 필드 정보 조회 섹션에서 확인할 수 있다.
  • queryset : team 모델을 가져올 조건을 설정할 수 있다.
  • queryset.select_related : Team 모델과 관련된 Color 모델을 추가로 조회하기 위해 작성했다. 마찬가지로 prefetch_related도 여러 번 체이닝 가능할 것이다.
  • to_attr: 쿼리를 여러 번 호출해야하는 경우 메모리에 저장해준다고 한다. 또한 이 이름이 serializer로 전달되는 값이 된다.

 

Viewset에서 queryset 정의

queryset = Company.objects.filter(...)
.prefetch_related(
    Prefetch(
        "team_set",
        queryset=Team.objects.all()
        .select_related("color"),
        to_attr="team_info")
)

 

Serializer에서 값 정의

class MemberListSerializer(serializers.ModelSerializer):
    name = serializers.CharField()
    team_info = TeamBaseSerializer(many=True, read_only=True)

 


 

모델 필드 정보 조회

 

Python console에서 사용가능하다.

 

{모델명}._meta.fields : 해당 모델의 기본 필드 정보를 보여준다.

{모델명}._meta.related_objects : 해당 모델과 연관된 모델들의 정보를 보여준다. 여기서 역참조로 정의된 '{모델명}_set' 이름을 확인할 수 있다.

 

 


 

prefetch 관련 규칙

 

- prefetch를 여러 depth로 chaning하고 싶다면 Prefetch 객체의 query_set에서 prefetch_related를 반복적으로 사용하면 된다. 이 경우 앞선 prefetch의 filtering 조건이 적용된 부분을 포함하여 하위 prefetch에 적용되므로 추가로 계속해서 하위 prefetch문의 queryset에서 filtering을 신경쓰지는 않아도 된다.

 

- to_attr로 _set의 이름을 변경해주자

예를 들어 아래 코드에서 schoolclassmapping_set은 school에서 바라보는 schoolclassmapping을 지칭하는 기본 명칭이다. 이대로 사용하면 Related Manager에 의해 None값이 표시될 수 있으므로 to_attr을 통해 이름을 변경해준다. 이후 아래에서처럼 Serializer에서도 변경된 to_attr 이름으로 조회하면 된다.

 

return (
    super().get_queryset().prefetch_related(
        Prefetch(
            "school",
            queryset=School.objects.filter(
                type=self.get_school_type(),
            ).prefetch_related(
                Prefetch(
                    "schoolclassmapping_set",
                    queryset=SchoolClassMapping.objects.all().prefetch_related(
                        Prefetch(
                            "teacher",
                            queryset=Teacher.objects.all(),
                            to_attr="teacher_by_school",
                        )
                    ),
                    to_attr="schooclassmapping_set_by_type",
                ),
            ),
            to_attr="school_set_filter_by_type",
        )
    )
)

 

class TeacherListSerializer(serializers.ModelSerializer):
    school_name = serializers.CharField(source="school_filter_by_type.name", read_only=True,)
    school_region = serializers.SerailizerMethodField()
    
 def get_school_region(self, obj):
 	return obj.school_filter_by_type.data.get('region')

 

728x90
반응형