1) To make queries, make a GET request with query parameters in the payload
res = self.client.get(
RECIPES_URL,
{'tags': f'{tag1.id}',
'ingredients': f'{ingredient1.id}'}
)
2) Process the queries in the get_queryset method in the RecipeViewset
# helper function
# _ prefix is used to indicate private function
def _params_to_int(self, qs):
"""convert a list of string IDs to ints"""
return [int(str_id) for str_id in qs.split(',')]
def get_queryset(self):
# filtering based on params in payload
# returns none if params are not available
tags = self.request.query_params.get('tags')
ingredients = self.request.query_params.get('ingredients')
queryset = self.queryset
if tags:
tag_ids = self._params_to_int(tags)
# djangos filter for attrs
queryset = queryset.filter(tags__id__in=tag_ids)
if ingredients:
ingredient_ids = self._params_to_int(ingredients)
queryset = queryset.filter(ingredients__id__in=ingredient_ids)
return queryset.filter(user=self.request.user)
API tests¶
In test_rcipe_api.py
# test filtering recipes
def test_filter_recipes_by_tag(self):
recipe1 = sample_recipe(user=self.user, title='Thai veg curry')
recipe2 = sample_recipe(user=self.user, title='Italian Tahini')
tag1 = sample_tag(user=self.user, name='Vegan')
tag2 = sample_tag(user=self.user, name='Vegetarian')
recipe1.tags.add(tag1)
recipe2.tags.add(tag2)
recipe3 = sample_recipe(user=self.user, title='fish and chips')
res = self.client.get(
RECIPES_URL,
{'tags': f'{tag1.id}, {tag2.id}'}
)
serializer1 = RecipeSerializer(recipe1)
serializer2 = RecipeSerializer(recipe2)
serializer3 = RecipeSerializer(recipe3)
self.assertIn(serializer1.data, res.data)
self.assertIn(serializer2.data, res.data)
self.assertNotIn(serializer3.data, res.data)
# test filtering tags and ingredients
def test_filter_recipes_by_tag_and_ingredient(self):
recipe1 = sample_recipe(user=self.user, title='posh beans on toast')
recipe2 = sample_recipe(user=self.user, title='chicken cocoinote')
tag1 = sample_tag(user=self.user, name='veg')
tag2 = sample_tag(user=self.user, name='non veg')
ingredient1 = sample_ingredient(user=self.user, name='beans')
recipe1.tags.add(tag1)
recipe1.ingredients.add(ingredient1)
recipe2.tags.add(tag2)
res = self.client.get(
RECIPES_URL,
{'tags': f'{tag1.id}',
'ingredients': f'{ingredient1.id}'}
)
serializer1 = RecipeSerializer(recipe1)
serializer2 = RecipeSerializer(recipe2)
self.assertIn(serializer1.data, res.data)
self.assertNotIn(serializer2.data, res.data)
In TagsViewSet
def get_queryset(self):
assigned_only = bool(self.request.query_params.get('assigned_only'))
queryset = self.queryset
if assigned_only:
# Django also allows access of reverse relation in foreign keys
queryset = queryset.filter(recipe__isnull=False)
return queryset.filter(user=self.request.user).order_by('-name')
API test¶
In test_tags_api.py
# filter tags assigned to recipes
def test_retrieve_tags_assigned_to_recipes(self):
"""retrieve tags that are assigned to some recipe"""
tag1 = Tag.objects.create(user=self.user, name='Breakfast')
tag2 = Tag.objects.create(user=self.user, name='Lunch')
recipe = Recipe.objects.create(
title='Coriander egg toast',
time_miniutes=10,
price=3.00,
user=self.user
)
recipe.tags.add(tag1)
res = self.client.get(TAGS_URL, {'assigned_only': 1})
serializer1 = TagSerializer(tag1)
serializer2 = TagSerializer(tag2)
self.assertIn(serializer1.data, res.data)
self.assertNotIn(serializer2.data, res.data)