API for querying database

A) API for filtering tags and ingredients in Recipe

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)

B) API for Filtering tags that are assined to some recipe

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)