こんにちは!
このライブラリを使用することはとても嬉しいことです! 私はそれが提供するすべての便利さにとても興奮しています!
有難うございます!
今の私の目標は、モデルパラメータの検索機能と順序付けもサポートするエンドポイントが必要です。
私がしている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"
]
この問題は、まったく注文していないように見えることです。 理由がわかりません。 それはとても奇妙です。
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)
@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
}
]
}
これについての興味深い部分は、今、私が一番上に注文したい1つの学校がこの検索結果にないことです。 これは、これが注文された可能性が高いことを意味しますが、ページ付けはまだ結果を相殺しています。 しかし、私が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
ます。 これがこれを達成するための正しい方法であるかどうかはわかりませんが、うまくいくようです😂
私が行う提案の1つは、リクエストの実際のURLの例または単純なテキストを使用してドキュメントで明確にし、例のo
が注文のパラメーターとして公開されていることを説明することです。
o
パラメーターが順序付けフィルターに割り当てられるものであることを明確にしたので、正しく機能しているように見えます。
もしquerysets
私が提供していますfilter
すでにそれらに注釈付きフィールドを持っている-それは私のテストから、見えます、私は私自身のカスタムすることを義務付けておりませんことをOrderingFilter
。
そのため、代わりに単純なOrderingFilter
使用できると思います。
私が間違っている場合は私を訂正してください!
私はそれが私の混乱を解決すると信じています! ありがとうございました!
こんにちは@loganknecht。 スーパー、あなたはそれが機能しているように見えます。 💃
アノテーションを使用している場合は、標準クラスyesを使用してアノテーションをフィルタリングおよび順序付けできるはずです。
ドキュメントを微調整します。
ご意見ありがとうございます。
@carltongibsonと他のみんな、素晴らしいライブラリに感謝します!
最も参考になるコメント
こんにちは@loganknecht。 素晴らしい問題報告をありがとうございました。 それはうまく始まり、そしてただ良くなります。 とても明確です。 だからありがとう。
まず最初に:
SchoolOrderingFilter
をフィールド名o
定義したようです:だからあなたが言うとき:
クエリ文字列パラメータも
o
になると思います。 これは機能しますか:{{API_URL}}/api/v1/schools/?offset=5&limit=3&o=learner_enrolled_count
?それから(あなたのレポートで私が確信していないビット)あなたはそれが_計算されたフィールド_であると言います-あなたは正確に何を意味しますか? つまり、モデルに表示されるフィールドですか、それともPythonプロパティが言いますか?
クエリセットの
order_by()
がこのフィールドで機能するかどうか(の一部)を尋ねる別の方法はありますか? つまり、これは機能しますか?