Поиск в Django с помощью оператора AND с отношением многие ко многим

Это моя модель

class MenuItem(models.Model):
    name = models.CharField(max_length=500, null=False)
    description = models.CharField(max_length=500, null=True)
    image_url = models.CharField(max_length=1000, null=True)
    menu_category = models.ForeignKey(MenuCategory, on_delete=models.CASCADE)

    def __str__(self):
        return f'{self.name}'


class Venue(models.Model):
    name = models.CharField(max_length=500, null=False)

    def __str__(self):
        return f'{self.name}'


class VenueMenu(models.Model):
    venue = models.ForeignKey(Venue, null=False, on_delete=models.CASCADE)
    menu_item = models.ManyToManyField(MenuItem, null=False)

Это мое мнение

@api_view(['GET'])
def search_menu_item(request):
    if request.GET.get('venue') and request.GET.get('search_name'):
        menu_item_filter = Q(menu_item__name__icontains=request.GET.get('search_name'))
        venue_filter = Q(venue__name=request.GET.get('venue').title())
        menu_item_search = VenueMenu.objects.filter(venue_filter & menu_item_filter)
        serializer = VenueMenuSerializer(menu_item_search, many=True)
        return Response(serializer.data)

Это мой сериализатор

class MenuItemSerializer(serializers.ModelSerializer):
    menu_category = MenuCategorySerializer(many=True)

    class Meta:
        model = MenuItem
        fields = '__all__'


class VenueSerializer(serializers.ModelSerializer):
    class Meta:
        model = Venue
        fields = '__all__'


class VenueMenuSerializer(serializers.ModelSerializer):
    menu_item = MenuItemSerializer(many=True)
    venue = VenueSerializer(many=False)

Я хочу найти информацию об одном пункте меню в определенном месте, как вы можете видеть в моем запросе, я запрашиваю объект, но этот запрос возвращает мне все пункты меню, связанные с этим местом, включая тот, который я искал, но я хочу, чтобы один пункт меню, который я ищу, был связан с этим местом.

Вы получаете правильно отфильтрованные VenueMenu, но связанные MenuItem не фильтруются автоматически, потому что фильтр предназначен для VenueMenu, а не для MenuItem.

Чтобы отфильтровать связанные MenuItem в сериализаторе, вам придется сделать фильтрованную предварительную выборку, используя Prefetch, как например:

from django.db.models import Prefetch


menu_item_search = VenueMenu.objects.filter(
    venue_filter & menu_item_filter
).prefetch_related(
    Prefetch(
        'menu_item',
        queryset=MenuItem.objects.filter(name__icontains=request.GET.get('search_name'))
    )
)

Для дальнейшего улучшения можно также выбрать связанные Venue с помощью select_related, чтобы избежать выполнения отдельного запроса только для получения деталей места проведения в сериализаторе. Итак, в целом:

menu_item_search = VenueMenu.objects.filter(
    venue_filter & menu_item_filter,
).select_related(
    'venue',
).prefetch_related(
    Prefetch(
        'menu_item',
        queryset=MenuItem.objects.filter(name__icontains=request.GET.get('search_name'))
    )
)
Вернуться на верх