Skip to content

Commit

Permalink
implemented functionality of Tag API
Browse files Browse the repository at this point in the history
  • Loading branch information
nishu-saini committed Nov 5, 2023
1 parent e216fa4 commit d085d77
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 10 deletions.
50 changes: 42 additions & 8 deletions app/recipe/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,55 @@
)


class TagSerializer(serializers.ModelSerializer):
"""Serializer for tags"""

class Meta:
model = Tag
fields = ['id', 'name']
read_only_fields = ['id']


class RecipeSerializer(serializers.ModelSerializer):
"""Serializer for recipes"""
tags = TagSerializer(many=True, required=False)

class Meta:
model = Recipe
fields = ['id', 'title', 'time_minutes', 'price', 'link']
fields = ['id', 'title', 'time_minutes', 'price', 'link', 'tags']
read_only_fields = ['id']

def _get_or_create_tags(self, tags, recipe):
"""Handle getting or creating tags as needed"""
auth_user = self.context['request'].user
for tag in tags:
tag_obj, created = Tag.objects.get_or_create(
user=auth_user,
**tag
)
recipe.tags.add(tag_obj)

def create(self, validate_data):
"""Create a recipe"""
tags = validate_data.pop('tags', [])
recipe = Recipe.objects.create(**validate_data)
self._get_or_create_tags(tags, recipe)

return recipe

def update(self, instance, validated_data):
"""Update recipe"""
tags = validated_data.pop('tags', None)

if tags is not None:
instance.tags.clear()
self._get_or_create_tags(tags, instance)

for attr, value in validated_data.items():
setattr(instance, attr, value)

instance.save()
return instance

class RecipeDetailSerializer(RecipeSerializer):
"""Serializer for recipe detail view"""
Expand All @@ -25,11 +66,4 @@ class Meta(RecipeSerializer.Meta):
fields = RecipeSerializer.Meta.fields + ['description']


class TagSerializer(serializers.ModelSerializer):
"""Serializer for tags"""

class Meta:
model = Tag
fields = ['id', 'name']
read_only_fields = ['id']

112 changes: 111 additions & 1 deletion app/recipe/tests/test_recipe_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from rest_framework import status
from rest_framework.test import APIClient

from core.models import Recipe
from core.models import Recipe, Tag

from recipe.serializers import (
RecipeSerializer,
Expand Down Expand Up @@ -201,3 +201,113 @@ def test_delete_other_users_recipe_error(self):

self.assertEqual(res.status_code, status.HTTP_404_NOT_FOUND)
self.assertTrue(Recipe.objects.filter(id=recipe.id).exists())

def test_create_recipe_with_new_tags(self):
"""Test creating a recipe with new tags"""
payload = {
'title': 'Thai Prawn Curry',
'time_minutes': 30,
'price': Decimal('2.50'),
'tags': [
{ 'name': 'Thai' },
{ 'name': 'Dinner' },
]
}

res = self.client.post(RECIPE_URL, payload, format='json')

self.assertEqual(res.status_code, status.HTTP_201_CREATED)
recipes = Recipe.objects.filter(user=self.user)
self.assertEqual(recipes.count(), 1)

recipe = recipes[0]
self.assertEqual(recipe.tags.count(), 2)
for tag in payload['tags']:
exists = recipe.tags.filter(
name=tag['name'],
user=self.user
).exists()

self.assertTrue(exists)

def test_create_recipe_with_existing_tags(self):
"""Test creating a recipe with existing tag"""
tag_indian = Tag.objects.create(user=self.user, name='Indian')
payload = {
'title': 'Pongal',
'time_minutes': 60,
'price': Decimal('4.50'),
'tags': [
{ 'name': 'Indian' },
{ 'name': 'Breakfast' }
]
}

res = self.client.post(RECIPE_URL, payload, format='json')

self.assertEqual(res.status_code, status.HTTP_201_CREATED)
recipes = Recipe.objects.filter(user=self.user)

self.assertEqual(recipes.count(), 1)
recipe = recipes[0]
self.assertEqual(recipe.tags.count(), 2)
self.assertIn(tag_indian, recipe.tags.all())

for tag in payload['tags']:
exists = recipe.tags.filter(
name=tag['name'],
user=self.user
).exists()

self.assertTrue(exists)

def test_create_tag_on_update(self):
"""Test creating tag when updating a recipe"""
recipe = create_recipe(user=self.user)

payload = {
'tags': [
{'name': 'Lunch'},
]
}
url = detail_url(recipe.id)
res = self.client.patch(url, payload, format='json')

self.assertEqual(res.status_code, status.HTTP_200_OK)
new_tag = Tag.objects.get(user=self.user, name='Lunch')
self.assertIn(new_tag, recipe.tags.all())

def test_update_recipe_assign_tag(self):
"""Test assigning an existing tag when updating a recipe"""
tag_breakfast = Tag.objects.create(user=self.user, name='Breakfast')
recipe = create_recipe(user=self.user)
recipe.tags.add(tag_breakfast)

tag_lunch = Tag.objects.create(user=self.user, name='Lunch')
payload = {
'tags': [
{'name': 'Lunch'},
]
}

url = detail_url(recipe.id)
res = self.client.patch(url, payload, format='json')

self.assertEqual(res.status_code, status.HTTP_200_OK)
self.assertIn(tag_lunch, recipe.tags.all())
self.assertNotIn(tag_breakfast, recipe.tags.all())

def test_clear_recipe_tags(self):
"""Test clearing a recipes tags"""
tag = Tag.objects.create(user=self.user, name='Dessert')
recipe = create_recipe(user=self.user)
recipe.tags.add(tag)

payload = {'tags': []}
url = detail_url(recipe.id)
res = self.client.patch(url, payload, format='json')

self.assertEqual(res.status_code, status.HTTP_200_OK)
self.assertEqual(recipe.tags.count(), 0)


28 changes: 28 additions & 0 deletions app/recipe/tests/test_tags_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
TAGS_URL = reverse('recipe:tag-list')


def detail_url(tag_id):
"""Create and return a tag detail url"""
return reverse('recipe:tag-detail', args=[tag_id])


def create_user(email='user@example.com', password='testpass123'):
"""Create and return a user"""
return get_user_model().objects.create_user(email=email, password=password)
Expand Down Expand Up @@ -68,5 +73,28 @@ def test_tags_limited_to_user(self):
self.assertEqual(res.data[0]['name'], tag.name)
self.assertEqual(res.data[0]['id'], tag.id)

def test_update_tag(self):
"""Test updating a tag"""
tag = Tag.objects.create(user=self.user, name='After Dinner')

payload = {'name': 'Dessert'}
url = detail_url(tag.id)
res = self.client.patch(url, payload)

self.assertEqual(res.status_code, status.HTTP_200_OK)
tag.refresh_from_db()
self.assertEqual(tag.name, payload['name'])

def test_delete_tag(self):
"""Test deleting a tag"""
tag = Tag.objects.create(user=self.user, name='Breakfast')

url = detail_url(tag.id)
res = self.client.delete(url)

self.assertEqual(res.status_code, status.HTTP_204_NO_CONTENT)
tags = Tag.objects.filter(user=self.user)
self.assertFalse(tags.exists())



7 changes: 6 additions & 1 deletion app/recipe/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ def perform_create(self, serializer):
serializer.save(user=self.request.user)


class TagViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
class TagViewSet(
mixins.DestroyModelMixin,
mixins.UpdateModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet
):
"""Manage tags in the database"""
serializer_class = serializers.TagSerializer
queryset = Tag.objects.all()
Expand Down

0 comments on commit d085d77

Please sign in to comment.