Hallo! Vielen Dank für diese Bibliothek! Es war so ein Vergnügen, es mit meiner aktuellen API zu testen!
Ich habe zwei Modelle, ein School
und ein SchoolCourse
. SchoolCourse
hat eine umgekehrte Viele-zu-Eins-Beziehung zu School
.
Ich möchte in der Lage sein, das Objekt School
basierend auf dem name
des SchoolCourse
zu filtern
Als erweitertes Ziel würde ich außerdem gerne nach SchoolLesson
filtern können.
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
Welche - wenn Sie mit diesen Modellen in der Debug-Ansicht spielen, werden Sie dies sehen
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'
Der Filter, den ich gemacht habe, scheint ziemlich einfach zu sein.
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"
]
Dies funktioniert jedoch nicht. Wenn ich diese Anfrage stelle:
{{API_URL}}/api/v1/schools/?course_name=Nihongo
Das bekomme ich als Antwort zurück
{
"schools": []
}
Was mache ich hier falsch?
Interpretiere ich Django-Filter falsch? Interpretiere ich falsch, wie man die Datenmodelle verbindet?
Jede Anleitung ist willkommen!
Hallo @loganknecht. Können Sie nur als Plausibilitätsprüfung überprüfen, ob Ihre URL korrekt ist? In Ihrer Beispiel-URL beginnt die Abfragezeichenfolge mit einem /
statt mit einem ?
.
Ah @rpkilby - Entschuldigung, das war ein Versehen meinerseits durch Kopieren und Einfügen von Postman
. Die URL ist eigentlich {{API_URL}}/api/v1/schools/?offset=49&course_name=Nihongo
, aber ich habe die offset
entfernt, um weniger verwirrend zu sein, und es ist mir gelungen, das Gegenteil zu tun 😂
Ihr Beispiel wirft für mich keine offensichtlichen Probleme auf. Meine beste Vermutung ist, dass es ein Problem mit der API-Ansicht gibt. Haben Sie DjangoFilterBackend
zu filter_backends
$ Ihrer Einstellungen/Ansicht hinzugefügt? Und haben Sie filterset_class = SchoolFilter
gesetzt (beachten Sie, dass dies früher filter_class
war)?
Hallo @rpkilby
Hier ist der Ansichtscode, den ich habe
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
In pdb.set_trace()
habe ich das obige Verhör gepostet.
Ich habe DjangoFilterBackend
nicht festgelegt, da ich davon ausgegangen bin, dass es sich um eine globale Konfiguration handelt, und ich dies nur an einem einzelnen Endpunkt teste.
Außerdem habe ich dies aus dem gleichen Grund auch nicht für die Klasse festgelegt.
Es ist wichtig zu wissen, dass ich diesen Filter für den Namen von SchoolModel
verwenden KANN, aber wenn ich für den umgekehrten Fremdschlüssel school_course
filtere, funktioniert es nicht.
Gotcha - Sie erstellen das Filterset also direkt in der Ansicht. In diesem Fall müssten Sie die Filter-Backend-/Filterset-Klasse nicht festlegen.
Sie können überprüfen, ob die Daten korrekt aussehen, ob Fehler aufgetreten sind oder ob die SQL-Abfrage richtig aufgebaut ist. Versuchen:
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 Hier ist, was ich sehe
<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 Das sieht so aus, als ob es ein Problem mit meiner Paginator-Implementierung sein könnte, die so aussieht
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)
Wenn Sie eine Lösung sehen, lassen Sie es mich bitte wissen. Bleiben Sie dran, während ich versuche, das abzuschütteln.
Meine Vermutung ist, dass dies mit dem angegebenen Offset zusammenhängt. Es gibt nur 1 Objekt im gefilterten Abfragesatz, aber die Anfrage weist den Paginator an, die ersten 49 Elemente zu überspringen. Wenn Sie überprüfen würden
print(filtered_school_models[49:])
Ich nehme an, Sie würden <QuerySet []>
bekommen.
Schließen, wie es aussieht, ist ein Paginierungsproblem.
Hallo @rpkilby
Ich möchte Ihnen nur so sehr dafür danken, dass Sie mich durch dieses Gespräch geführt haben. Das war nicht einmal ein django-filter
Problem. Ich war nur ein Dingus. Du Rubber Ducking me ist super super geschätzt.
Sie sind fantastisch, und noch einmal vielen Dank für diese fantastische Bibliothek!
Keine Bange. Freue mich zu helfen!
Hilfreichster Kommentar
Meine Vermutung ist, dass dies mit dem angegebenen Offset zusammenhängt. Es gibt nur 1 Objekt im gefilterten Abfragesatz, aber die Anfrage weist den Paginator an, die ersten 49 Elemente zu überspringen. Wenn Sie überprüfen würden
Ich nehme an, Sie würden
<QuerySet []>
bekommen.Schließen, wie es aussieht, ist ein Paginierungsproblem.