Как отфильтровать по параметрам запроса, отношение многим ко многим
Хочу отфильтровать по параметрам запроса (по id).
К примеру: http://.../themes/?category=1&level=1
model.py
class Themes(models.Model):
category = models.ForeignKey(Categories, on_delete=models.CASCADE)
level = models.ManyToManyField(Level)
views.py
class ThemesListView(generics.ListAPIView):
serializer_class = ThemesListSerializer
def get_queryset(self):
queryset = Themes.objects.all()
category = self.request.query_params.get('category')
level = self.request.query_params.get('level')
if category:
queryset = queryset.filter(category_id=category)
if level:
queryset = queryset.filter(level=level)
return queryset
Ожидаю:
{
"id": 1,
"category": 1,
"level": [
1
]
}
Получаю:
{
"id": 1,
"category": 1,
"level": [
1,
2
]
}
Подскажите пожалуйста, как можно исправить код?
Ответы (1 шт):
Проблема возникает из-за того, что все предикаты добавленные в queryset с помощью filter, используется только для фильтрации объектов, которые возвращает queryset, т.е. в даном случае Themes.
Это значит, что выполняется запрос "верни мне все Themes, у которых есть такой то level". Данные для модели Level достаются отдельными запросами уже когда происходит сериализация Themes и идет доступ к полю level.
Чтоб это исправить нужно сконфигурировать запрос так, чтоб ассоциированные Level доставались в том же запросе (ну и при этом наложить фильтр, который укажет выбирать не все). Для этого нужно воспользоваться prefetch_related как-то так:
from django.db.models import Prefetch
...
if level:
# тут предполагается, что фильтруем по полю id в Level
queryset = queryset.prefetch_related(
Prefetch('level', queryset=Level.objects.filter(id=level)))l