Django-filter: لا يمكن تصفية المفتاح الخارجي العكسي

تم إنشاؤها على ٢٢ يونيو ٢٠٢٠  ·  10تعليقات  ·  مصدر: carltongibson/django-filter

مقدمة

مرحبا! شكرا جزيلا على هذه المكتبة لك! لقد كان من دواعي سروري اختباره باستخدام API الحالي الخاص بي!

هدفك الحالي

لدي نموذجان ، نموذج School و SchoolCourse . SchoolCourse له علاقة معكوسة من عدة إلى واحد إلى School .

أريد أن أكون قادرًا على تصفية الكائن School بناءً على name من SchoolCourse

بالإضافة إلى ذلك كهدف ممتد ، أود أن أكون قادرًا على التصفية بمقدار SchoolLesson في النهاية أيضًا.

نموذج البيانات

نموذج المدرسة

class SchoolModel(Model):
    name = CharField(max_length=24, unique=True)

    REQUIRED_FIELDS = ["name"]

    class Meta:
        ordering = ("id",)

    def get_courses(self):
        school_courses = SchoolCourseModel.objects.filter(school=self)
        return school_courses

دورة مدرسية

class SchoolCourseModel(Model):
    description = CharField(default="", max_length=200)
    name = CharField(max_length=50)
    school = ForeignKey(SchoolModel,
                        related_name="school_course",
                        on_delete=CASCADE)

    REQUIRED_FIELDS = ["school", "name"]

    class Meta:
        ordering = ("id",)
        unique_together = ("school", "name",)

    def get_lessons(self):
        school_lessons = SchoolLessonModel.objects.filter(course=self)
        return school_lessons

والتي - إذا لعبت بهذه النماذج في عرض التصحيح ، فسترى هذا

all_school_models[50].school_course
(Pdb++) <django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager object at 0x10a8177d0>
all_school_models[50].school_course.all()
(Pdb++) <QuerySet [<SchoolCourseModel: SchoolCourseModel object (151)>, <SchoolCourseModel: SchoolCourseModel object (152)>, <SchoolCourseModel: SchoolCourseModel object (153)>]>
all_school_models[50].school_course.all()[0]
(Pdb++) <SchoolCourseModel: SchoolCourseModel object (151)>
all_school_models[50].school_course.all()[0].name
(Pdb++) 'Nihongo Course One'

تكوين المرشح

يبدو المرشح الذي صنعته واضحًا إلى حد ما.

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

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

مشكلة

ومع ذلك، هذا لا يعمل. إذا قدمت هذا الطلب:
{{API_URL}}/api/v1/schools/?course_name=Nihongo

أحصل على هذا كردة فعل

{
    "schools": []
}

أسئلة

ما الخطأ الذي أفعله هنا؟

هل أسيء تفسير مرشحات django؟ هل أسيء تفسير كيفية توصيل نماذج البيانات؟

هو موضع تقدير أي توجيه!

التعليق الأكثر فائدة

أظن أن هذا مرتبط بالموازنة المحددة. يوجد عنصر واحد فقط في مجموعة طلبات البحث التي تمت تصفيتها ، ولكن الطلب يخبر الفاصل الزمني بتخطي أول 49 عنصرًا. إذا كنت تريد التحقق

print(filtered_school_models[49:])

أفترض أنك ستحصل على <QuerySet []> .

الإغلاق ، حيث يبدو أن هذه مشكلة ترقيم الصفحات.

ال 10 كومينتر

مرحبًاloganknecht. كإجراء فحص سلامة ، هل يمكنك التحقق من صحة عنوان URL الخاص بك؟ في مثال عنوان URL الخاص بك ، تبدأ سلسلة الاستعلام بـ / بدلاً من ? .

آه rpkilby - معذرةً ، لقد كان هذا سهوًا من جانبي من النسخ واللصق من Postman . عنوان url هو في الواقع {{API_URL}}/api/v1/schools/?offset=49&course_name=Nihongo لكنني كنت أزيل offset لأكون أقل إرباكًا ونجحت في القيام بالعكس 😂

المثال الخاص بك لا يثير أي مشاكل واضحة بالنسبة لي. أفضل تخميني هو أن هناك بعض المشكلات في عرض API. هل أضفت DjangoFilterBackend إلى إعداداتك / طريقة العرض filter_backends ؟ وهل قمت بتعيين filterset_class = SchoolFilter (لاحظ أن هذا كان filter_class

يا rpkilby

هذا هو رمز العرض لدي

class SchoolViewSet(ViewSet):
    http_method_names = ["get", "post"]

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

        filtered_school_models = SchoolFilter(request.GET, queryset=all_school_models)
        # import pdb
        # pdb.set_trace()

        paginator = HeaderLinkPagination()
        current_page_results = paginator.paginate_queryset(filtered_school_models.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

pdb.set_trace() هو المكان الذي نشرت فيه الاستجواب أعلاه.

لم أقم بتحديد DjangoFilterBackend حيث افترضت أن ذلك كان تكوينًا عامًا وأنا أختبر هذا فقط على نقطة نهاية واحدة.

بالإضافة إلى ذلك ، لم أقم بتعيين هذا للفصل للسبب نفسه.

من المهم معرفة أنه يمكنني استخدام هذا الفلتر على اسم SchoolModel لكن عندما أقوم بالتصفية على المفتاح الخارجي العكسي school_course فإنه لا يعمل.

مسكتك - لذلك تقوم بإنشاء مجموعة المرشحات في العرض مباشرة. في هذه الحالة ، نعم ، لن تحتاج إلى ضبط فئة المرشحات الخلفية / مجموعة المرشحات.

يمكنك التحقق لمعرفة ما إذا كانت البيانات تبدو صحيحة ، أو ما إذا كانت هناك أية أخطاء ، أو ما إذا تم تكوين استعلام SQL بشكل صحيح. يحاول:

filtered_school_models = SchoolFilter(request.GET, queryset=all_school_models)
print(filtered_school_models.data)
print(filtered_school_models.errors)
print(filtered_school_models.qs)
print(str(filtered_school_models.qs.query))

rpkilby إليكم ما أراه

<QueryDict: {'offset': ['49'], 'course_name': ['Nihongo']}>

<QuerySet [<SchoolModel: SchoolModel object (51)>]>
SELECT "piano_gym_api_schoolmodel"."id", "piano_gym_api_schoolmodel"."name", "piano_gym_api_schoolmodel"."school_board_id" FROM "piano_gym_api_schoolmodel" INNER JOIN "piano_gym_api_schoolcoursemodel" ON ("piano_gym_api_schoolmodel"."id" = "piano_gym_api_schoolcoursemodel"."school_id") WHERE UPPER("piano_gym_api_schoolcoursemodel"."name"::text) LIKE UPPER(%Nihongo%) ORDER BY "piano_gym_api_schoolmodel"."id" ASC

rpkilby يبدو أنه قد يكون هناك مشكلة في تطبيق ترقيم الصفحات الخاص بي والذي يبدو كالتالي

class HeaderLinkPagination(LimitOffsetPagination):
    default_limit = settings.DEFAULT_LIMIT
    max_limit = settings.DEFAULT_MAX_LIMIT
    min_limit = settings.DEFAULT_MIN_LIMIT
    min_offset = settings.DEFAULT_MIN_OFFSET
    max_offset = settings.DEFAULT_MAX_OFFSET

    def get_paginated_response(self, data):
        next_url = self.get_next_link()
        previous_url = self.get_previous_link()

        links = []
        header_data = (
            (previous_url, "prev"),
            (next_url, "next"),
        )
        for url, label in header_data:
            if url is not None:
                links.append("<{}>; rel=\"{}\"".format(url, label))

        headers = {"Link": ", ".join(links)} if links else {}

        return Response(data, headers=headers)

    def paginate_queryset(self, queryset, request, view=None):

        limit = request.query_params.get("limit")
        offset = request.query_params.get("offset")

        if limit is None:
            limit = settings.DEFAULT_LIMIT

        if offset is None:
            offset = settings.DEFAULT_OFFSET

        limit = int(limit)
        if limit > self.max_limit:
            error_message = ("Limit should be less than or equal to {0}"
                             ).format(self.max_limit)
            errors = {"limit": [error_message]}
            raise ValidationError(errors)
        elif limit < self.min_limit:
            error_message = ("Limit should be greater than or equal to {0}"
                             ).format(self.min_limit)
            errors = {"limit": [error_message]}
            raise ValidationError(errors)

        offset = int(offset)
        if offset > self.max_offset:
            error_message = ("Offset should be less than or equal to {0}"
                             ).format(self.max_offset)
            errors = {"offset": [error_message]}
            raise ValidationError(errors)
        elif offset < self.min_offset:
            error_message = ("Offset should be greater than or equal to {0}"
                             ).format(self.min_offset)
            errors = {"offset": [error_message]}
            raise ValidationError(errors)
        import pdb
        pdb.set_trace()

        return super(self.__class__, self).paginate_queryset(queryset, request, view)

إذا رأيت حلاً ، من فضلك أعلمني. ابق على اتصال بينما أحاول التخلص من هذا.

أظن أن هذا مرتبط بالموازنة المحددة. يوجد عنصر واحد فقط في مجموعة طلبات البحث التي تمت تصفيتها ، ولكن الطلب يخبر الفاصل الزمني بتخطي أول 49 عنصرًا. إذا كنت تريد التحقق

print(filtered_school_models[49:])

أفترض أنك ستحصل على <QuerySet []> .

الإغلاق ، حيث يبدو أن هذه مشكلة ترقيم الصفحات.

يا rpkilby

أريد فقط أن أقول لك شكراً جزيلاً على التحدث معي من خلال هذا. لم تكن هذه مشكلة حتى django-filter . كنت مجرد زنج. أنت تحط من المطاط لي هو موضع تقدير كبير.

أنت رائع ، ومرة ​​أخرى ، أشكرك على هذه المكتبة الرائعة!

لا قلق. سررت بالمساعدة!

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات