자신의 필드 값을 이용한 annotate 처리

6434 단어 파이썬장고

하고 싶은 일


class MyModel(models.Model):
    code = models.CharField(max_length=10)

이런 느낌의 모델로,



이런 식으로 같은 코드가 설정되어 있는 수를 세고 싶다.

순진한 해법과 문제점


class MyModelAdmin(admin.ModelAdmin):
    list_display = (
        'id',
        'code',
        'code_count',
    )

    def code_count(self, obj):
        return MyModel.objects.filter(code=obj.code).count()

admin이나 model에 메소드 추가하면 할 수는 있지만



이런 식으로 레코드의 수만큼 sql이 발행된다.
(n+1 문제 발생)

Subquery와 OuterRef를 사용한 annotate





Subquery와 OuterRef를 사용하여 자신의 코드를 사용한 subquery를 발행하면 된다.
from django.contrib import admin
from django.db.models import Count, OuterRef, Subquery

from .models import MyModel


@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
    list_display = (
        'id',
        'code',
        'code_count',
    )

    def get_queryset(self, request):
        sub_query = MyModel.objects.filter(
            code=OuterRef('code')
        ).values('code').annotate(count=Count('code')).values('count')
        return MyModel.objects.annotate(code_count=Subquery(sub_query))

    def code_count(self, obj):
        return obj.code_count
code=OuterRef('code') 에서 바깥쪽에 있는 자신의 코드로 짜낸다.
code의 수로 카운트를 취하고 싶기 때문에 .values('code')annotate(Count('code')) 로 Group화한다.

sub_query의 결과는 1개가 아니면 안 되므로, 마지막으로 .values('count') 그리고 code 의 카운트수만 돌려준다.

그리고는 code_count 라는 이름으로 앞서 만든 sub_query를 annotate 해 준다.

이것으로 object에 code_count 라는 코드 건수의 attribute가 붙기 때문에, method로 그 값을 돌려주면 완성.



행 수분 발행되고 있던 query가 1개에 정리되었다.

발행되는 sql문은 이런 느낌.



외부 코드를 사용하여 내부에서 code_count를 만들고 있음을 알 수 있습니다.

조사해도 몰랐는데 코드 쓴 후에 아래의 키워드로 구그 하면 같은 코드가 발견되었다.
django count outerref

좋은 웹페이지 즐겨찾기