Поиск в 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'))
)
)