Django-filter: Tidak Dapat Memfilter Kunci Asing Terbalik

Dibuat pada 22 Jun 2020  ·  10Komentar  ·  Sumber: carltongibson/django-filter

pengantar

Halo! Terima kasih banyak untuk perpustakaan ini! Sangat menyenangkan untuk mengujinya dengan API saya saat ini!

Tujuan Saat Ini

Saya memiliki dua model, School dan SchoolCourse . SchoolCourse memiliki hubungan terbalik banyak ke satu ke School .

Saya ingin dapat memfilter objek School berdasarkan name dari SchoolCourse

Selain itu, sebagai tujuan tambahan, saya juga ingin dapat memfilter menurut SchoolLesson pada akhirnya.

Model data

Model Sekolah

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

Kursus Sekolah

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

Yang mana - jika Anda bermain dengan model ini dalam tampilan debug, Anda akan melihat ini

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'

Konfigurasi Filter

Filter yang saya buat tampaknya cukup mudah.

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

Masalah

Namun ini tidak berhasil. Jika saya membuat permintaan ini:
{{API_URL}}/api/v1/schools/?course_name=Nihongo

Saya mendapatkan ini kembali sebagai tanggapan

{
    "schools": []
}

pertanyaan

Apa yang saya lakukan salah di sini?

Apakah saya salah mengartikan Django-filters? Apakah saya salah mengartikan cara menghubungkan model data?

Setiap bimbingan dihargai!

Komentar yang paling membantu

Dugaan saya adalah ini terkait dengan offset yang diberikan. Hanya ada 1 objek dalam kumpulan kueri yang difilter, tetapi permintaan tersebut memberi tahu paginator untuk melewati 49 item pertama. Jika Anda ingin memeriksa

print(filtered_school_models[49:])

Saya berasumsi Anda akan mendapatkan <QuerySet []> .

Penutupan, karena sepertinya ini adalah masalah pagination.

Semua 10 komentar

Hai @loganknecht. Sama seperti pemeriksaan kewarasan, dapatkah Anda memverifikasi bahwa URL Anda benar? Di URL contoh Anda, string kueri dimulai dengan / alih-alih ? .

Ah @rpkilby - maaf, ini adalah kesalahan saya dari menyalin dan menempel dari Postman . Urlnya sebenarnya {{API_URL}}/api/v1/schools/?offset=49&course_name=Nihongo tapi saya menghapus offset agar tidak terlalu membingungkan dan berhasil melakukan yang sebaliknya 😂

Contoh Anda tidak menimbulkan masalah yang jelas bagi saya. Tebakan terbaik saya adalah ada beberapa masalah dengan tampilan API. Sudahkah Anda menambahkan DjangoFilterBackend ke pengaturan/tampilan Anda filter_backends ? Dan apakah Anda sudah menetapkan filterset_class = SchoolFilter (perhatikan bahwa ini dulu filter_class )?

Hai @rpkilby

Ini kode tampilan yang saya miliki

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() adalah tempat saya memposting interogasi di atas.

Saya tidak menetapkan DjangoFilterBackend karena saya berasumsi bahwa itu adalah konfigurasi global dan saya hanya menguji ini pada satu titik akhir.

Selain itu, saya juga tidak mengatur ini untuk kelas karena alasan yang sama.

Penting untuk diketahui bahwa SAYA BISA menggunakan filter ini atas nama SchoolModel tetapi ketika saya memfilter pada kunci asing terbalik school_course itu tidak berfungsi.

Gotcha - jadi Anda membuat filterset dalam tampilan secara langsung. Dalam hal ini, ya, Anda tidak perlu mengatur kelas backend/filterset filter.

Anda mungkin memeriksa untuk melihat apakah data terlihat benar, jika ada kesalahan, atau jika kueri SQL dibuat dengan benar. Mencoba:

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 Inilah yang saya lihat

<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 Ini sepertinya ada masalah dengan implementasi paginator saya yang terlihat seperti ini

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)

Jika Anda melihat solusi, beri tahu saya. Tetap disini sementara saya mencoba untuk menghilangkan ini.

Dugaan saya adalah ini terkait dengan offset yang diberikan. Hanya ada 1 objek dalam kumpulan kueri yang difilter, tetapi permintaan tersebut memberi tahu paginator untuk melewati 49 item pertama. Jika Anda ingin memeriksa

print(filtered_school_models[49:])

Saya berasumsi Anda akan mendapatkan <QuerySet []> .

Penutupan, karena sepertinya ini adalah masalah pagination.

Hai @rpkilby

Saya hanya ingin mengucapkan terima kasih banyak untuk berbicara saya melalui ini. Ini bahkan bukan masalah django-filter . Aku hanya menjadi dingus. Anda karet merunduk saya sangat dihargai.

Anda luar biasa, dan sekali lagi, terima kasih untuk perpustakaan yang luar biasa ini!

Jangan khawatir. Saya senang bisa membantu!

Apakah halaman ini membantu?
0 / 5 - 0 peringkat