أهلا بك!
لقد كان من دواعي سروري استخدام هذه المكتبة! أنا سعيد جدًا بكل وسائل الراحة التي يوفرها!
شكرا لك على ذلك!
هدفي الآن هو أنني أرغب في الحصول على نقطة نهاية تدعم إمكانية البحث في معلمات النموذج ، والترتيب أيضًا.
لدي نموذج 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-options
بناءً على ذلك ، كتبت مجموعة التصفية هذه هنا:
# ------------------------------------------------------------------------------
# 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
أرى أن 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
}
]
}
الجزء المثير للاهتمام حول هذا الأمر ، الآن هو أن المدرسة الوحيدة التي أريد ترتيبها في الأعلى ليست في نتيجة البحث هذه. مما يعني أنه من المحتمل أن يكون هذا الأمر قد تم طلبه ولكن ترقيم الصفحات يوازن النتائج. ومع ذلك ، عندما استخدمت 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
bit موجود في المتغير query_set_to_return
. ما يفعله هو نقل count
من إجمالي المتعلمين المسجلين و annotates
إلى الحقل learner_enrolled_count
. ليس لدي أي فكرة عما إذا كانت هذه هي الطريقة الصحيحة لتحقيق ذلك ، ولكن يبدو أنها تعمل 😂
أحد الاقتراحات التي أود تقديمها هو التوضيح في الوثائق ، إما باستخدام أمثلة url الفعلية للطلبات أو نص بسيط وشرح أن o
في الأمثلة هو ما يتم عرضه كمعامل للطلب.
يبدو أنه يعمل بشكل صحيح الآن بعد أن أوضحت أن المعلمة o
هي ما يتم تعيين عامل تصفية الطلب له!
إذا كان querysets
الذي أقدمه لـ filter
يحتوي بالفعل على حقل مشروح به - يبدو ، من الاختبار ، أنني لست ملزمًا بعمل مخصصي OrderingFilter
.
لهذا السبب أعتقد أنه يمكنني استخدام OrderingFilter
بسيطًا بدلاً من ذلك!
أرجوا أن تصحح لي إذا كنت مخطئا!
أعتقد أن هذا يحل حيرتي! شكرا لك!
مرحبًاloganknecht. رائع ، يبدو أنك قد نجحت في ذلك. 💃
إذا كنت تستخدم تعليقًا توضيحيًا ، فيجب أن تكون قادرًا على تصفية ذلك وطلبه باستخدام الفصل القياسي نعم.
سأقوم بتعديل المستندات.
شكرا لمساهمتك.
carltongibson والجميع ، شكرًا على المكتبة الرائعة!
التعليق الأكثر فائدة
مرحبًاloganknecht. يجب أن أقول ، شكرًا لك على تقرير المشكلة الرائع هذا. يبدأ بشكل جيد ، ويتحسن. فقط واضح جدا. لذا شكرا لك.
أولاً: يبدو أنك عرّفت
SchoolOrderingFilter
باسم الحقلo
:لذلك عندما تقول:
أتوقع أن يكون معامل سلسلة الاستعلام
o
أيضًا. هل هذا يعمل:{{API_URL}}/api/v1/schools/?offset=5&limit=3&o=learner_enrolled_count
؟ثم (الشيء الذي لست متأكدًا منه في تقريرك) أنت تقوله - حقل محسوب - ماذا تقصد بالضبط؟ أي هل هو حقل يظهر في النموذج ، أم أنها إحدى خصائص Python؟
هناك طريقة أخرى للسؤال (جزء من) ألا وهي هل يعمل
order_by()
في مجموعة طلبات البحث مع هذا الحقل؟ أي هل هذا العمل: