Django-filter: 'рдСрд░реНрдбрд░рд┐рдВрдЧ рдлрд╝рд┐рд▓реНрдЯрд░' рдФрд░ рдкрд░рд┐рдХрд▓рд┐рдд рдлрд╝реАрд▓реНрдб рдХреЗ рд▓рд┐рдП рдЕрд╕реНрдкрд╖реНрдЯ рдЙрдкрдпреЛрдЧ

рдХреЛ рдирд┐рд░реНрдорд┐рдд 18 рдЬреБрд▓ре░ 2020  ┬╖  4рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: carltongibson/django-filter

рдХреГрддрдЬреНрдЮрддрд╛

рдирдорд╕реНрддреЗ!

рдЗрд╕ рдкреБрд╕реНрддрдХрд╛рд▓рдп рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рд▓рдЧрд╛! рдореИрдВ рдЗрд╕рдХреЗ рджреНрд╡рд╛рд░рд╛ рдкреНрд░рджрд╛рди рдХреА рдЬрд╛рдиреЗ рд╡рд╛рд▓реА рд╕рднреА рд╕реБрд╡рд┐рдзрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╣реБрдд рд░реЛрдорд╛рдВрдЪрд┐рдд рд╣реВрдВ!

рдЙрд╕рдХреЗ рд▓рд┐рдпреЗ рдЖрдкрдХрд╛ рдзрдиреНрдпрд╡рд╛рдж!

рд▓рдХреНрд╖реНрдп

рдореЗрд░рд╛ рд▓рдХреНрд╖реНрдп рдЕрднреА рдпрд╣ рд╣реИ рдХрд┐ рдореИрдВ рдПрдХ рд╕рдорд╛рдкрди рдмрд┐рдВрджреБ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдЬреЛ рдореЙрдбрд▓ рдорд╛рдкрджрдВрдбреЛрдВ рдкрд░ рдЦреЛрдЬ-рдХреНрд╖рдорддрд╛ рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИ, рдФрд░ рд╕рд╛рде рд╣реА рд╕рд╛рде рдЖрджреЗрд╢ рджреЗрддрд╛ рд╣реИред

рдбреЗрдЯрд╛ рдореЙрдбрд▓

рдореЗрд░реЗ рдкрд╛рд╕ рдПрдХ School рдореЙрдбрд▓ рд╣реИ рдЬрд┐рд╕ рдкрд░ рдПрдХ рдкрд░рд┐рдХрд▓рд┐рдд рдлрд╝реАрд▓реНрдб рд╣реИ рдЬрд┐рд╕реЗ learner_enrolled_count рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ

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"
        ]

рдпрд╣ рдореБрджреНрджрд╛ рдпрд╣ рд╣реИ рдХрд┐ рдпрд╣ рдмрд┐рд▓реНрдХреБрд▓ рднреА рдЖрджреЗрд╢ рдирд╣реАрдВ рджреЗ рд░рд╣рд╛ рд╣реИ! рдореБрдЭреЗ рдХреЛрдИ рдЬрд╛рдирдХрд╛рд░реА рдирд╣реАрдВ рд╣реИ рдХреА рдХреНрдпреЛрдВред рдпрд╣ рдХрд┐рддрдиреА рдЕрдЬреАрдм рдмрд╛рдд рд╣реИред

рдЕрдЧрд░ рдореИрдВ filter рдХреА 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 ?

рдлрд┐рд░ (рдореИрдВ рдЖрдкрдХреА рд░рд┐рдкреЛрд░реНрдЯ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдереЛрдбрд╝рд╛ рдЕрдирд┐рд╢реНрдЪрд┐рдд рд╣реВрдВ) рдЖрдк рдХрд╣рддреЗ рд╣реИрдВ it┬┤sa _computed field_ -- рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЖрдкрдХрд╛ рдХреНрдпрд╛ рдорддрд▓рдм рд╣реИ? рдпрд╛рдиреА рдпрд╣ рдПрдХ рдРрд╕рд╛ рдХреНрд╖реЗрддреНрд░ рд╣реИ рдЬреЛ рдореЙрдбрд▓ рдкрд░ рджрд┐рдЦрд╛рдИ рджреЗрддрд╛ рд╣реИ, рдпрд╛ рдпрд╣ рдПрдХ рдкрд╛рдпрдерди рд╕рдВрдкрддреНрддрд┐ рд╣реИ?

рдкреВрдЫрдиреЗ рдХрд╛ рдПрдХ рдФрд░ рддрд░реАрдХрд╛ (рдЗрд╕рдХрд╛ рд╣рд┐рд╕реНрд╕рд╛) рдЗрд╕ рдХреНрд╖реЗрддреНрд░ рдХреЗ рд╕рд╛рде рдХреНрд╡реЗрд░реАрд╕реЗрдЯ рдХрд╛рдо рдкрд░ 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 ?

рдлрд┐рд░ (рдореИрдВ рдЖрдкрдХреА рд░рд┐рдкреЛрд░реНрдЯ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдереЛрдбрд╝рд╛ рдЕрдирд┐рд╢реНрдЪрд┐рдд рд╣реВрдВ) рдЖрдк рдХрд╣рддреЗ рд╣реИрдВ it┬┤sa _computed field_ -- рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЖрдкрдХрд╛ рдХреНрдпрд╛ рдорддрд▓рдм рд╣реИ? рдпрд╛рдиреА рдпрд╣ рдПрдХ рдРрд╕рд╛ рдХреНрд╖реЗрддреНрд░ рд╣реИ рдЬреЛ рдореЙрдбрд▓ рдкрд░ рджрд┐рдЦрд╛рдИ рджреЗрддрд╛ рд╣реИ, рдпрд╛ рдпрд╣ рдПрдХ рдкрд╛рдпрдерди рд╕рдВрдкрддреНрддрд┐ рд╣реИ?

рдкреВрдЫрдиреЗ рдХрд╛ рдПрдХ рдФрд░ рддрд░реАрдХрд╛ (рдЗрд╕рдХрд╛ рд╣рд┐рд╕реНрд╕рд╛) рдЗрд╕ рдХреНрд╖реЗрддреНрд░ рдХреЗ рд╕рд╛рде рдХреНрд╡реЗрд░реАрд╕реЗрдЯ рдХрд╛рдо рдкрд░ 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 ред рдореБрдЭреЗ рдирд╣реАрдВ рдкрддрд╛ рдХрд┐ рдпрд╣ рд╣рд╛рд╕рд┐рд▓ рдХрд░рдиреЗ рдХрд╛ рдпрд╣ рд╕рд╣реА рддрд░реАрдХрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ

рд╕реБрдЭрд╛рд╡

рдПрдХ рд╕реБрдЭрд╛рд╡ рдореИрдВ рдкреНрд░рд▓реЗрдЦрди рдореЗрдВ рд╕реНрдкрд╖реНрдЯ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ, рдпрд╛ рддреЛ рдЕрдиреБрд░реЛрдзреЛрдВ рдпрд╛ рд╕рд░рд▓ рдкрд╛рда рдХреЗ рд▓рд┐рдП рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдпреВрдЖрд░рдПрд▓ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдФрд░ рд╕рдордЭрд╛рдПрдВ рдХрд┐ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдореЗрдВ o рдСрд░реНрдбрд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ рд░реВрдк рдореЗрдВ рдЙрдЬрд╛рдЧрд░ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред

рдирд┐рд╖реНрдХрд░реНрд╖

рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЕрдм рдпрд╣ рд╕рд╣реА рдврдВрдЧ рд╕реЗ рдХрд╛рдо рдХрд░ рд░рд╣рд╛ рд╣реИ рдХрд┐ рдЖрдкрдиреЗ o рдкреИрд░рд╛рдореАрдЯрд░ рдХреЛ рд╕реНрдкрд╖реНрдЯ рдХрд░ рджрд┐рдпрд╛ рд╣реИ рдХрд┐ рдСрд░реНрдбрд░рд┐рдВрдЧ рдлрд╝рд┐рд▓реНрдЯрд░ рдХреЛ рдХреНрдпрд╛ рд╕реМрдВрдкрд╛ рдЧрдпрд╛ рд╣реИ!

рдпрджрд┐ querysets рдореИрдВ filter рд▓рд┐рдП рдкреНрд░рджрд╛рди рдХрд░ рд░рд╣рд╛ рд╣реВрдВ рддреЛ рдЙрдирдореЗрдВ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдПрдХ рдПрдиреЛрдЯреЗрдЯ рдлрд╝реАрд▓реНрдб рд╣реИ - рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ, рдореЗрд░реЗ рдкрд░реАрдХреНрд╖рдг рд╕реЗ, рдХрд┐ рдореИрдВ рдЕрдкрдирд╛ рд╕реНрд╡рдпрдВ рдХрд╛ рдХрд╕реНрдЯрдо OrderingFilter рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдмрд╛рдзреНрдп рдирд╣реАрдВ рд╣реВрдВ

рддреЛ рдЙрд╕рдХреЗ рдХрд╛рд░рдг рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдореИрдВ рдЗрд╕рдХреЗ рдмрдЬрд╛рдп рдПрдХ рд╕рд╛рдзрд╛рд░рдг OrderingFilter рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ!

рдореБрдЭреЗ рд╕рд╣реА рдЬрд╡рд╛рдм рджреЛ рдЕрдЧрд░ рдореИрдВ рдЧрд▓рдд рд╣реВрдБ!

рдореБрдЭреЗ рд╡рд┐рд╢реНрд╡рд╛рд╕ рд╣реИ рдХрд┐ рдпрд╣ рдореЗрд░реА рдЙрд▓рдЭрди рдХреЛ рд╣рд▓ рдХрд░рддрд╛ рд╣реИ! рд╢реБрдХреНрд░рд┐рдпрд╛!

рд╣рд╛рдп @loganknechtред рд╕реБрдкрд░, рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЖрдкрдиреЗ рдЗрд╕реЗ рдХрд╛рдо рдХрд░ рд▓рд┐рдпрд╛ рд╣реИред рдореИрдВ

рдпрджрд┐ рдЖрдк рдПрдиреЛрдЯреЗрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣реЗ рд╣реИрдВ рддреЛ рдЖрдк рдорд╛рдирдХ рд╡рд░реНрдЧ рд╣рд╛рдБ рдХреЗ рд╕рд╛рде рдЙрд╕ рдкрд░ рдлрд╝рд┐рд▓реНрдЯрд░ рдФрд░ рдСрд░реНрдбрд░ рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред

рдореИрдВ рдбреЙрдХреНрд╕ рдХреЗ рд▓рд┐рдП рдПрдХ рдЯреНрд╡реАрдХ рдХрд░реВрдБрдЧрд╛ред

рдЖрдкрдХреЗ рд╕рд╣рдпреЛрдЧ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рджред

@carltongibson рдФрд░ рдмрд╛рдХреА рд╕рднреА, рдЕрджреНрднреБрдд рдкреБрд╕реНрддрдХрд╛рд▓рдп рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рдж!

рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
0 / 5 - 0 рд░реЗрдЯрд┐рдВрдЧреНрд╕

рд╕рдВрдмрдВрдзрд┐рдд рдореБрджреНрджреЛрдВ

GuillaumeCisco picture GuillaumeCisco  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

gotexis picture gotexis  ┬╖  4рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

sassanh picture sassanh  ┬╖  4рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

blueyed picture blueyed  ┬╖  4рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

inakrin picture inakrin  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ