Salut! Merci beaucoup pour cette bibliothèque ! Ce fut un tel plaisir de le tester avec mon API actuelle !
J'ai deux modèles, un School
et un SchoolCourse
. SchoolCourse
a une relation inverse plusieurs à un avec School
.
Je veux pouvoir filtrer l'objet School
en fonction du name
du SchoolCourse
De plus, en tant qu'objectif étendu, j'aimerais pouvoir filtrer par SchoolLesson
éventuellement aussi.
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
Qui - si vous jouez avec ces modèles dans la vue de débogage, vous verrez ceci
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'
Le filtre que j'ai fait semble assez simple.
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"
]
Cependant, cela ne fonctionne pas. Si je fais cette demande :
{{API_URL}}/api/v1/schools/?course_name=Nihongo
Je reçois ceci comme réponse
{
"schools": []
}
Qu'est-ce que je fais de mal ici ?
Est-ce que j'interprète mal les filtres Django ? Est-ce que j'interprète mal comment connecter les modèles de données ?
Toute orientation est appréciée!
Salut @loganknecht. Pouvez-vous vérifier que votre URL est correcte ? Dans votre exemple d'URL, la chaîne de requête commence par un /
au lieu d'un ?
.
Ah @rpkilby - excuses, c'était un oubli de ma part en copiant et collant de Postman
. L'url est en fait {{API_URL}}/api/v1/schools/?offset=49&course_name=Nihongo
mais j'enlevais le offset
pour être moins déroutant et j'ai réussi à faire le contraire 😂
Votre exemple ne me pose aucun problème évident. Ma meilleure hypothèse est qu'il y a un problème avec la vue API. Avez-vous ajouté le DjangoFilterBackend
aux $# filter_backends
1$#$ de vos paramètres/vue ? Et avez-vous défini filterset_class = SchoolFilter
(notez que c'était auparavant filter_class
) ?
Salut @rpkilby
Voici le code de vue que j'ai
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
Le pdb.set_trace()
est l'endroit où j'ai posté l'interrogation ci-dessus.
Je n'ai pas défini le DjangoFilterBackend
car je supposais qu'il s'agissait d'une configuration globale et je ne teste cela que sur un seul point de terminaison.
De plus, je n'ai pas défini cela pour la classe non plus pour la même raison.
Il est important de savoir que je PEUX utiliser ce filtre sur le nom du SchoolModel
mais lorsque je filtre sur la clé étrangère inversée school_course
cela ne fonctionne pas.
Gotcha - vous créez donc directement le jeu de filtres dans la vue. Dans ce cas, oui, vous n'auriez pas besoin de définir la classe filter backend/filterset.
Vous pouvez vérifier si les données semblent correctes, s'il y a eu des erreurs ou si la requête SQL est correctement formée. Essayer:
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 Voici ce que je vois
<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 Cela ressemble à un problème avec mon implémentation de paginateur qui ressemble à ceci
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)
Si vous voyez une solution, merci de m'en faire part. Restez à l'écoute pendant que j'essaie de résoudre ce problème.
Je suppose que cela est lié au décalage donné. Il n'y a qu'un seul objet dans le jeu de requêtes filtré, mais la demande indique au paginateur d'ignorer les 49 premiers éléments. Si vous deviez vérifier
print(filtered_school_models[49:])
Je suppose que vous obtiendriez <QuerySet []>
.
Fermeture, car il semble que ce soit un problème de pagination.
Salut @rpkilby
Je veux juste dire merci beaucoup de m'avoir parlé à travers ça. Ce n'était même pas un problème django-filter
. J'étais juste un dingus. Votre esquive en caoutchouc est super super appréciée.
Vous êtes incroyable, et encore une fois, merci pour cette incroyable bibliothèque !
Pas de soucis. Heureux d'aider!
Commentaire le plus utile
Je suppose que cela est lié au décalage donné. Il n'y a qu'un seul objet dans le jeu de requêtes filtré, mais la demande indique au paginateur d'ignorer les 49 premiers éléments. Si vous deviez vérifier
Je suppose que vous obtiendriez
<QuerySet []>
.Fermeture, car il semble que ce soit un problème de pagination.