Django-filter: 'OrderingFilter' 및 κ³„μ‚°λœ ν•„λ“œμ˜ 뢈λͺ…ν™•ν•œ μ‚¬μš©λ²•

에 λ§Œλ“  2020λ…„ 07μ›” 18일  Β·  4μ½”λ©˜νŠΈ  Β·  좜처: carltongibson/django-filter

κ³ λ§ˆμ›€

μ•ˆλ…•ν•˜μ„Έμš”!

이 라이브러리λ₯Ό μ‚¬μš©ν•˜λŠ” 것은 정말 μ¦κ±°μ› μŠ΅λ‹ˆλ‹€! λ‚˜λŠ” 그것이 μ œκ³΅ν•˜λŠ” λͺ¨λ“  νŽΈμ˜μ— λŒ€ν•΄ 맀우 κΈ°μ©λ‹ˆλ‹€!

κ°μ‚¬ν•©λ‹ˆλ‹€!

λͺ©ν‘œ

μ§€κΈˆ 제 λͺ©ν‘œλŠ” λͺ¨λΈ λ§€κ°œλ³€μˆ˜μ— λŒ€ν•œ 검색 κΈ°λŠ₯κ³Ό μ£Όλ¬Έ κΈ°λŠ₯을 μ§€μ›ν•˜λŠ” μ—”λ“œν¬μΈνŠΈλ₯Ό κ°–κ³  μ‹ΆμŠ΅λ‹ˆλ‹€.

데이터 λͺ¨λΈ

learner_enrolled_count λΌλŠ” κ³„μ‚°λœ ν•„λ“œκ°€ μžˆλŠ” School λͺ¨λΈμ΄ μžˆμŠ΅λ‹ˆλ‹€.

JSON 응닡은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

{
    "schools": [
        {
            "id": 6,
            "name": "Piano Gym Six",
            "courses": [
                // ...
            ],
            "learner_enrolled_count": 0
        },
        {
            "id": 7,
            "name": "Piano Gym Seven",
            "courses": [
                // ...
            ],
            "learner_enrolled_count": 5
        }
    ]
}

learner_enrolled_count λŠ” κ³„μ‚°λœ ν•„λ“œμž…λ‹ˆλ‹€.

문제

μ—¬κΈ°μ—μ„œ λ¬Έμ„œλ₯Ό μ½μ—ˆμŠ΅λ‹ˆλ‹€.
https://django-filter.readthedocs.io/en/stable/ref/filters.html?highlight=order#orderingfilter
그리고 μ—¬κΈ°:
https://django-filter.readthedocs.io/en/stable/ref/filters.html?highlight=order#adding -custom-filter-choices

κ·Έλž˜μ„œ 여기에 κΈ°λ°˜ν•˜μ—¬ 이 ν•„ν„° μ„ΈνŠΈλ₯Ό μž‘μ„±ν–ˆμŠ΅λ‹ˆλ‹€.

# ------------------------------------------------------------------------------
# Python Standard Libraries
# ------------------------------------------------------------------------------
# N/A
# ------------------------------------------------------------------------------
# Third-party Libraries
# ------------------------------------------------------------------------------
from django_filters import CharFilter
from django_filters import OrderingFilter
from django_filters.rest_framework import FilterSet
# ------------------------------------------------------------------------------
# Custom Libraries
# ------------------------------------------------------------------------------
from piano_gym_api.versions.v1.models.school_model import SchoolModel

# See:
# https://django-filter.readthedocs.io/en/stable/ref/filters.html#adding-custom-filter-choices
class SchoolOrderingFilter(OrderingFilter):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.extra["choices"] += [
            ("learner_enrolled_count", "Learner Enrolled Count"),
            ("-learner_enrolled_count", "Learner Enrolled Count (descending)"),
        ]

    def filter(self, query_set, values):
        if(values is None):
            return super().filter(query_set, values)

        for value in values:
            if value in ['learner_enrolled_count', '-learner_enrolled_count']:
                return query_set.order_by(value)

        return super().filter(query_set, values)


class SchoolFilter(FilterSet):
    school_name = CharFilter(field_name="name",
                             lookup_expr="icontains")
    # ---
    course_name = CharFilter(field_name="school_course__name",
                             lookup_expr="icontains")
    course_description = CharFilter(field_name="school_course__description",
                                    lookup_expr="icontains")
    # ---
    lesson_name = CharFilter(field_name="school_lesson__name",
                             lookup_expr="icontains")
    lesson_description = CharFilter(field_name="school_lesson__description",
                                    lookup_expr="icontains")
    # ---
    flash_card_set_name = CharFilter(field_name="school_lesson__flash_card_set__name",
                                     lookup_expr="icontains")
    flash_card_set_description = CharFilter(field_name="school_lesson__flash_card_set__description",
                                            lookup_expr="icontains")
    # ---
    headmaster_username = CharFilter(field_name="school_board__school_board_headmaster__learner__user__username",
                                     lookup_expr="icontains")
    board_member_username = CharFilter(field_name="school_board__school_board_member__learner__user__username",
                                       lookup_expr="icontains")

    # See:
    # https://django-filter.readthedocs.io/en/stable/ref/filters.html#orderingfilter
    o = SchoolOrderingFilter(
        # tuple-mapping retains order
        fields=(
            ("learner_enrolled_count", "learner_enrolled_count"),
        ),

        # labels do not need to retain order
        field_labels={
            "learner_enrolled_count": "Total learners enrolled in school",
        }
    )

    class Meta:
        model = SchoolModel
        fields = [
            "school_name",
            # ---
            "course_name",
            "course_description",
            # ---
            "lesson_name",
            "lesson_description",
            # ---
            "headmaster_username",
            "board_member_username"
        ]

이 λ¬Έμ œλŠ” 주문이 μ „ν˜€ λ˜μ§€ μ•ŠλŠ” 것 κ°™μŠ΅λ‹ˆλ‹€! 이유λ₯Ό λͺ¨λ₯΄κ² μŠ΅λ‹ˆλ‹€. λ„ˆλ¬΄ 이상해

SchoolOrderingFilter 의 filter λ©”μ„œλ“œμ— 디버그 좔적을 λ†“μœΌλ©΄ values κ°€ None μž„μ„ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. 그게 무엇인지 잘 λͺ¨λ₯΄κ² μŠ΅λ‹ˆλ‹€.

λ‚΄κ°€ λ§Œλ“œλŠ” μš”μ²­μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€
{{API_URL}}/api/v1/schools/?offset=5&limit=3&ordering=learner_enrolled_count

그리고 이 μš”μ²­μ„ λ°›λŠ” λ·°λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

class SchoolViewSet(ViewSet):
    # ...

    def list(self, request):
        all_school_models = SchoolModel.objects.getBrowseSchoolsData()

        school_filter = SchoolFilter(request.GET, queryset=all_school_models)

        paginator = HeaderLinkPagination()
        current_page_results = paginator.paginate_queryset(school_filter.qs,
                                                           request)

        all_schools_serializer = SchoolSerializer(current_page_results,
                                                  many=True)
        response_data = {
            "schools": all_schools_serializer.data
        }

        response_to_return = paginator.get_paginated_response(response_data)
        return response_to_return

질문

필터링 κΈ°λŠ₯을 μ‚¬μš©ν•˜λŠ” 방법과 κ³„μ‚°λœ ν•„λ“œ μˆœμ„œλ₯Ό μ‚¬μš©ν•˜λŠ” 방법에 λŒ€ν•œ μ„€λͺ…μ„œμ—μ„œ μ €μ—κ²ŒλŠ” 정말 λΆˆλΆ„λͺ…ν•˜λ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€.

λ‚΄κ°€ 무엇을 잘λͺ»ν•˜κ³  μžˆμ§€? 이 κΈ°λŠ₯을 잘λͺ» μ΄ν•΄ν•˜κ³  μžˆμŠ΅λ‹ˆκΉŒ? μ˜¬λ°”λ₯Έ 단계λ₯Ό μˆ˜ν–‰ν•˜κ³  μžˆλŠ” 것 κ°™μ§€λ§Œ 이 라이브러리의 ordering κΈ°λŠ₯이 μž‘λ™ν•˜μ§€ μ•ŠλŠ” 것 κ°™μŠ΅λ‹ˆλ‹€!

λ‹€μ‹œ ν•œ 번, λͺ¨λ“  것에 κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€!

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

μ•ˆλ…•ν•˜μ„Έμš” @loganknechtμž…λ‹ˆλ‹€. μ΄λ ‡κ²Œ 멋진 문제 λ³΄κ³ μ„œμ— κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€. 그것은 잘 μ‹œμž‘ν•˜κ³  점점 μ’‹μ•„μ§‘λ‹ˆλ‹€. λ„ˆλ¬΄ λͺ…ν™•ν•©λ‹ˆλ‹€. κ°μ‚¬ν•©λ‹ˆλ‹€.

λ¨Όμ € SchoolOrderingFilter ν•„λ“œ 이름을 o μ •μ˜ν•œ 것 κ°™μŠ΅λ‹ˆλ‹€.

o = SchoolOrderingFilter(...)

κ·Έλž˜μ„œ 당신이 말할 λ•Œ :

λ‚΄κ°€ λ§Œλ“œλŠ” μš”μ²­μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€
{{API_URL}}/api/v1/schools/?offset=5&limit=3&ordering=learner_enrolled_count

쿼리 λ¬Έμžμ—΄ λ§€κ°œλ³€μˆ˜λ„ o κ²ƒμœΌλ‘œ μ˜ˆμƒν•©λ‹ˆλ‹€. 이것이 μž‘λ™ν•©λ‹ˆκΉŒ: {{API_URL}}/api/v1/schools/?offset=5&limit=3&o=learner_enrolled_count ?

그런 λ‹€μŒ (λ‹Ήμ‹ μ˜ λ³΄κ³ μ„œμ—μ„œ λ‚΄κ°€ ν™•μ‹ ν•  수 μ—†λŠ” λΆ€λΆ„) 당신은 그것이 _κ³„μ‚°λœ ν•„λ“œ_라고 λ§ν•©λ‹ˆλ‹€ -- μ •ν™•νžˆ 무엇을 μ˜λ―Έν•©λ‹ˆκΉŒ? 즉, λͺ¨λΈμ— λ‚˜νƒ€λ‚˜λŠ” ν•„λ“œμž…λ‹ˆκΉŒ, μ•„λ‹ˆλ©΄ Python 속성이 λ§ν•˜λŠ” κ²ƒμž…λ‹ˆκΉŒ?

쿼리 μ„ΈνŠΈμ˜ order_by() κ°€ 이 ν•„λ“œμ—μ„œ μž‘λ™ν•˜λŠ”μ§€ λ¬»λŠ” (일뢀) λ‹€λ₯Έ 방법은 λ¬΄μ—‡μž…λ‹ˆκΉŒ? 즉, 이 μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.

SchoolModel.objects.filter(...).order_by(learner_enrolled_count)

λͺ¨λ“  4 λŒ“κΈ€

μ•ˆλ…•ν•˜μ„Έμš” @loganknechtμž…λ‹ˆλ‹€. μ΄λ ‡κ²Œ 멋진 문제 λ³΄κ³ μ„œμ— κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€. 그것은 잘 μ‹œμž‘ν•˜κ³  점점 μ’‹μ•„μ§‘λ‹ˆλ‹€. λ„ˆλ¬΄ λͺ…ν™•ν•©λ‹ˆλ‹€. κ°μ‚¬ν•©λ‹ˆλ‹€.

λ¨Όμ € SchoolOrderingFilter ν•„λ“œ 이름을 o μ •μ˜ν•œ 것 κ°™μŠ΅λ‹ˆλ‹€.

o = SchoolOrderingFilter(...)

κ·Έλž˜μ„œ 당신이 말할 λ•Œ :

λ‚΄κ°€ λ§Œλ“œλŠ” μš”μ²­μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€
{{API_URL}}/api/v1/schools/?offset=5&limit=3&ordering=learner_enrolled_count

쿼리 λ¬Έμžμ—΄ λ§€κ°œλ³€μˆ˜λ„ o κ²ƒμœΌλ‘œ μ˜ˆμƒν•©λ‹ˆλ‹€. 이것이 μž‘λ™ν•©λ‹ˆκΉŒ: {{API_URL}}/api/v1/schools/?offset=5&limit=3&o=learner_enrolled_count ?

그런 λ‹€μŒ (λ‹Ήμ‹ μ˜ λ³΄κ³ μ„œμ—μ„œ λ‚΄κ°€ ν™•μ‹ ν•  수 μ—†λŠ” λΆ€λΆ„) 당신은 그것이 _κ³„μ‚°λœ ν•„λ“œ_라고 λ§ν•©λ‹ˆλ‹€ -- μ •ν™•νžˆ 무엇을 μ˜λ―Έν•©λ‹ˆκΉŒ? 즉, λͺ¨λΈμ— λ‚˜νƒ€λ‚˜λŠ” ν•„λ“œμž…λ‹ˆκΉŒ, μ•„λ‹ˆλ©΄ Python 속성이 λ§ν•˜λŠ” κ²ƒμž…λ‹ˆκΉŒ?

쿼리 μ„ΈνŠΈμ˜ order_by() κ°€ 이 ν•„λ“œμ—μ„œ μž‘λ™ν•˜λŠ”μ§€ λ¬»λŠ” (일뢀) λ‹€λ₯Έ 방법은 λ¬΄μ—‡μž…λ‹ˆκΉŒ? 즉, 이 μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.

SchoolModel.objects.filter(...).order_by(learner_enrolled_count)

@carltongibson κ½€ ν₯λ―Έ o 이 μ •μ˜λœ ordering λ§€κ°œλ³€μˆ˜κ°€ 될 것이라고 μ˜ˆμƒν•˜μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€! λ‚˜λŠ” λ¬Έμ„œμ—μ„œ 그것을 μ΄ν•΄ν•˜μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€ πŸ˜‚

o λ§€κ°œλ³€μˆ˜ μ‚¬μš©

ν₯미둜운 점은 쿼리λ₯Ό μ‚¬μš©ν•˜λ©΄
{{API_URL}}/api/v1/schools/?offset=5&limit=3&o=learner_enrolled_count

λ‚˜λŠ” λ‹€μŒκ³Ό 같은 κ²°κ³Όλ₯Ό μ–»μŠ΅λ‹ˆλ‹€.

{
    "schools": [
        {
            "id": 6,
            "name": "Piano Gym Six",
            "courses": [
                # ...
            ],
            "learner_enrolled_count": 0
        },
        {
            "id": 8,
            "name": "Piano Gym Eight",
            "courses": [
                # ...
            ],
            "learner_enrolled_count": 0
        },
        {
            "id": 9,
            "name": "Piano Gym Nine",
            "courses": [
                # ...
            ],
            "learner_enrolled_count": 0
        }
    ]
}

이것에 λŒ€ν•œ ν₯미둜운 뢀뢄은 이제 λ‚΄κ°€ 맨 μœ„μ— μ£Όλ¬Έν•˜κ³  싢은 학ꡐ가 이 검색 결과에 μ—†λ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. 이것은 이것이 μ£Όλ¬Έλ˜μ—ˆμ„ κ°€λŠ₯성이 μžˆμ§€λ§Œ νŽ˜μ΄μ§€ 맀김이 κ²°κ³Όλ₯Ό μ—¬μ „νžˆ μƒμ‡„ν•˜κ³  μžˆμŒμ„ μ˜λ―Έν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ learner_enrolled_count μ‚¬μš©ν–ˆμ„ λ•Œ λ§ˆμ§€λ§‰ 지점에 μžˆμŠ΅λ‹ˆλ‹€.
{{API_URL}}/api/v1/schools/?limit=3&o=learner_enrolled_count

κ·Έλž˜μ„œ -learner_enrolled_count (μ˜€ν”„μ…‹ 제거 및 λ‚΄λ¦Όμ°¨μˆœ μ •λ ¬)λ₯Ό μ‚¬μš©ν•˜μ—¬ ν…ŒμŠ€νŠΈν–ˆμŠ΅λ‹ˆλ‹€.
{{API_URL}}/api/v1/schools/?limit=3&o=-learner_enrolled_count
그리고 이 응닡을 λ°›μ•˜μŠ΅λ‹ˆλ‹€

{
    "schools": [
        {
            "id": 7,
            "name": "Piano Gym Seven",
            "courses": [
                # ...
            ],
            "learner_enrolled_count": 1
        },
        {
            "id": 1,
            "name": "Piano Gym",
            "courses": [
                # ...
            ],
            "learner_enrolled_count": 0
        },
        {
            "id": 2,
            "name": "Piano Gym Two",
            "courses": [
                # ...
            ],
            "learner_enrolled_count": 0
        }
    ]
}

νš¨κ³Όκ°€ μžˆμ—ˆλ˜ 것 κ°™μŠ΅λ‹ˆλ‹€!

κ³„μ‚°λœ ν•„λ“œ

learner_enrolled_count 은(λŠ”) λͺ¨λΈ μžμ²΄μ— μ‘΄μž¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 주석을 μ‚¬μš©ν•˜μ—¬ 이것을 κ³„μ‚°ν•˜κ³  결과에 μ‚½μž…ν–ˆμŠ΅λ‹ˆλ‹€.

데이터 λͺ¨λΈμ„ λ„ˆλ¬΄ 많이 λ‹€λ£° μœ„ν—˜μ΄ μžˆλŠ” 이 끝점은 학ꡐ λͺ©λ‘μ— λŒ€ν•œ 쿼리λ₯Ό ν•„ν„°λ§ν•˜κ³  μ •λ ¬ν•˜κΈ° μœ„ν•œ 단일 μ§„μž…μ μž…λ‹ˆλ‹€.

λ‹€μŒκ³Ό 같은 λͺ¨λΈ 검색을 μ‚¬μš©ν•˜μ—¬ 이 끝점을 μ΅œμ ν™”ν•΄μ•Ό ν•©λ‹ˆλ‹€.

class SchoolModelManager(Manager):
    def getBrowseSchoolsData(self, *args, **kwargs):
        """Return all Schools that contain lessons with flash card sets.

        Does not exclude empty sets, just requires that the school has something
        to enroll in
        """
        # WARNING: This MUST be imported here otherwise the compilation fails
        #          because of circular dependencies
        from piano_gym_api.versions.v1.models.flash_card_model import FlashCardModel
        # from piano_gym_api.versions.v1.models.flash_card_model import PlaySheetMusicFlashCardModel
        # from piano_gym_api.versions.v1.models.flash_card_model import TrueOrFalseFlashCardModel
        # from piano_gym_api.versions.v1.models.flash_card_set_model import FlashCardSetModel
        from piano_gym_api.versions.v1.models.sheet_music_model import SheetMusicModel
        # import pdb
        # pdb.set_trace()

        # --------------------
        sheet_music_query_set = (SheetMusicModel.objects.all()
                                 .select_related("school"))
        play_sheet_music_flash_card_sheet_music_prefetch = Prefetch("playsheetmusicflashcardmodel__sheet_music",
                                                                    sheet_music_query_set)
        # --------------------
        flash_card_query_set = (FlashCardModel.objects.all()
                                .select_related("flash_card_set",
                                                "playsheetmusicflashcardmodel",
                                                "school",
                                                "trueorfalseflashcardmodel")
                                .prefetch_related(play_sheet_music_flash_card_sheet_music_prefetch))
        flash_card_prefetch = Prefetch("flash_card_set__flash_card", flash_card_query_set)
        # --------------------
        school_lesson_query_set = (SchoolLessonModel.objects.all()
                                   .select_related("course",
                                                   "flash_card_set",
                                                   "school")
                                   .prefetch_related(flash_card_prefetch))
        school_lesson_prefetch = Prefetch("school_lesson", school_lesson_query_set)
        # --------------------
        school_course_query_set = (SchoolCourseModel.objects.all()
                                   .select_related("school")
                                   .prefetch_related(school_lesson_prefetch))
        school_course_prefetch = Prefetch("school_course", school_course_query_set)
        # --------------------
        query_set_to_return = (SchoolModel.objects.filter(school_lesson__flash_card_set__isnull=False)
                               .distinct()
                               # .annotate(learner_enrolled_count=Count("learner_enrolled_school", distinct=True))
                               .annotate(learner_enrolled_count=Case(
                                   When(learner_enrolled_school__learner_enrolled_course__learner_enrolled_lesson__is_enrolled=True,
                                        then=1),
                                   default=0,
                                   output_field=IntegerField())
        ).prefetch_related(school_course_prefetch))

        return query_set_to_return

calculated field λΉ„νŠΈλŠ” query_set_to_return λ³€μˆ˜μ— μžˆμŠ΅λ‹ˆλ‹€. 그것이 ν•˜λŠ” 일은 λ“±λ‘λœ 총 ν•™μŠ΅μž 쀑 count λ₯Ό annotates learner_enrolled_count ν•„λ“œλ‘œ κ°€μ Έμ˜€λŠ” κ²ƒμž…λ‹ˆλ‹€. 이것이 이것을 λ‹¬μ„±ν•˜λŠ” μ˜¬λ°”λ₯Έ 방법인지 λͺ¨λ₯΄κ² μ§€λ§Œ μž‘λ™ν•˜λŠ” 것 κ°™μŠ΅λ‹ˆλ‹€ πŸ˜‚

μ œμ•ˆ

λ‚΄κ°€ μ œμ•ˆν•˜λŠ” ν•œ 가지 μ œμ•ˆμ€ μš”μ²­μ— λŒ€ν•œ μ‹€μ œ URL μ˜ˆμ œλ‚˜ κ°„λ‹¨ν•œ ν…μŠ€νŠΈλ₯Ό μ‚¬μš©ν•˜μ—¬ λ¬Έμ„œμ—μ„œ λͺ…ν™•νžˆ ν•˜κ³  예제의 o κ°€ 주문을 μœ„ν•œ λ§€κ°œλ³€μˆ˜λ‘œ λ…ΈμΆœλ˜λŠ” κ²ƒμž„μ„ μ„€λͺ…ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

κ²°λ‘ 

o λ§€κ°œλ³€μˆ˜κ°€ μˆœμ„œ 지정 ν•„ν„°κ°€ ν• λ‹Ήλ˜λŠ” λ§€κ°œλ³€μˆ˜μž„μ„ λͺ…ν™•νžˆ ν–ˆκΈ° λ•Œλ¬Έμ— 이제 μ˜¬λ°”λ₯΄κ²Œ μž‘λ™ν•˜λŠ” κ²ƒμœΌλ‘œ λ³΄μž…λ‹ˆλ‹€!

querysets λŒ€ν•΄ μ œκ³΅ν•˜λŠ” filter 이미 주석 ν•„λ“œκ°€ μžˆλŠ” 경우 - ν…ŒμŠ€νŠΈ κ²°κ³Ό, λ‚˜λ§Œμ˜ μ‚¬μš©μž μ •μ˜ OrderingFilter λ₯Ό λ§Œλ“€ μ˜λ¬΄κ°€ μ—†λŠ” κ²ƒμœΌλ‘œ λ³΄μž…λ‹ˆλ‹€

κ·Έλž˜μ„œ μ €λŠ” λŒ€μ‹  κ°„λ‹¨ν•œ OrderingFilter λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€!

λ‚΄κ°€ ν‹€λ Έλ‹€λ©΄ μ§€μ ν•΄μ£Όμ„Έμš”!

λ‚˜λŠ” 그것이 λ‚΄ ν˜Όλž€μ„ ν•΄κ²°ν•œλ‹€κ³  λ―ΏμŠ΅λ‹ˆλ‹€! κ°μ‚¬ν•©λ‹ˆλ‹€!

μ•ˆλ…•ν•˜μ„Έμš” @loganknechtμž…λ‹ˆλ‹€. 슈퍼, μž‘λ™ν•˜λŠ” 것 κ°™μŠ΅λ‹ˆλ‹€. πŸ’ƒ

주석을 μ‚¬μš©ν•˜λŠ” 경우 ν‘œμ€€ 클래슀 yesλ₯Ό μ‚¬μš©ν•˜μ—¬ 주석을 ν•„ν„°λ§ν•˜κ³  μ •λ ¬ν•  수 μžˆμ–΄μ•Ό ν•©λ‹ˆλ‹€.

λ¬Έμ„œλ₯Ό μˆ˜μ •ν•˜κ² μŠ΅λ‹ˆλ‹€.

μž…λ ₯ν•΄ μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€.

@carltongibson κ³Ό λ‹€λ₯Έ λͺ¨λ“  μ‚¬λžŒλ“€, λ†€λΌμš΄ λΌμ΄λΈŒλŸ¬λ¦¬μ— κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€!

이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰