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')
'Programming-[Backend] > Django' 카테고리의 다른 글
[TIL] django data migration 데이터 마이그레이션 (0) | 2022.12.17 |
---|---|
[TIL] FloatField, DecimalField 차이점 / 위경도 표시 소수점과 거리값 (0) | 2022.12.14 |
[TIL] django.conf settings, 환경 설정 정보 상대 참조하기 (0) | 2022.11.02 |
[TIL] Django ORM values, annotate, Subquery, OuterRef, JSONField, Type Cast 부분 적용 (2) | 2022.09.30 |
[TIL] 부모 Serializer field 값 제거 (0) | 2022.09.29 |