From 8774b8551b0093cabdecaf701a561d91ddcea9ed Mon Sep 17 00:00:00 2001 From: abhiabhi94 <13880786+abhiabhi94@users.noreply.github.com> Date: Thu, 12 Nov 2020 20:06:59 +0530 Subject: [PATCH] Fix API response - add serializers and return flag object in serialized form when a request of correct form is made. --- flag/api/serializers.py | 42 ++++++++++++++++++++++ flag/api/views.py | 17 ++++++--- flag/managers.py | 10 ++++-- flag/models.py | 6 ++++ flag/templatetags/flag_tags.py | 4 +-- tests/test_api.py | 64 +++++++++++++++++++++------------- tests/test_models.py | 53 ++++++++++++++++++++++------ 7 files changed, 152 insertions(+), 44 deletions(-) create mode 100644 flag/api/serializers.py diff --git a/flag/api/serializers.py b/flag/api/serializers.py new file mode 100644 index 0000000..458f033 --- /dev/null +++ b/flag/api/serializers.py @@ -0,0 +1,42 @@ +from django.contrib.auth import get_user_model +from rest_framework import serializers + +from flag.models import Flag + + +User = get_user_model() + + +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ('id', 'username') + lookup_field = 'username' + + +class FlagSerializer(serializers.ModelSerializer): + class Meta: + model = Flag + fields = ('creator', 'count', 'state', 'verbose_state', 'moderator', 'is_flagged', 'reporters') + read_only_fields = ('creator', 'moderator', 'reporters') + + creator = UserSerializer() + moderator = UserSerializer() + verbose_state = serializers.SerializerMethodField() + reporters = serializers.SerializerMethodField() + + def __init__(self, *args, **kwargs): + context = kwargs['context'] + self.model_obj = context.get('model_obj') + self.user = context.get('user') + super().__init__(*args, **kwargs) + + @staticmethod + def get_verbose_state(obj): + return obj.get_verbose_state(obj.state) + + def get_reporters(self, obj): + flag_obj = Flag.objects.get_flag(self.model_obj) + return [ + {'id': instance.user.id, 'username': instance.user.username} for instance in flag_obj.flags.all() + ] diff --git a/flag/api/views.py b/flag/api/views.py index 798e069..c11b778 100644 --- a/flag/api/views.py +++ b/flag/api/views.py @@ -3,6 +3,8 @@ from rest_framework.response import Response from flag.mixins import ContentTypeMixin +from flag.api.serializers import FlagSerializer +from flag.models import Flag from flag.utils import process_flagging_request @@ -10,15 +12,20 @@ class SetFlag(ContentTypeMixin, APIView): api = True permission_classes = (permissions.IsAuthenticated,) + def _get_serializer_context(self): + context = {} + context['model_obj'] = self.model_obj + context['user'] = self.request.user + return context + def post(self, request, *args, **kwargs): - self.validate(request) + self.validate(self.request) response = process_flagging_request(user=request.user, model_obj=self.model_obj, data=self.data) detail = {'detail': response['msg']} if response['status']: # 1 indicates bad request return Response(status=status.HTTP_400_BAD_REQUEST, data=detail) - if response.get('flag', None): - return Response(status=status.HTTP_201_CREATED, data=detail) - - return Response(status=status.HTTP_200_OK, data=detail) + flag = Flag.objects.get_flag(self.model_obj) + serializer = FlagSerializer(instance=flag, context=self._get_serializer_context()) + return Response(serializer.data, status=status.HTTP_200_OK) diff --git a/flag/managers.py b/flag/managers.py index 4aba5db..e5056f0 100644 --- a/flag/managers.py +++ b/flag/managers.py @@ -13,8 +13,10 @@ def get_flag(self, model_obj): flag, __ = self.get_or_create(content_type=ctype, object_id=model_obj.id, creator=creator) return flag + def is_flagged(self, model_obj): + flag = self.get_flag(model_obj) + return flag.is_flagged -class FlagInstanceManager(models.Manager): def has_flagged(self, user, model_obj): """ Returns whether a model object has been flagged by a user or not @@ -26,9 +28,11 @@ def has_flagged(self, user, model_obj): Returns: bool """ - ctype = get_content_type(model_obj) - return self.filter(flag__content_type=ctype, flag__object_id=model_obj.id, user=user).exists() + flag = self.get_flag(model_obj) + return flag.flags.filter(user=user).exists() + +class FlagInstanceManager(models.Manager): def _clean_reason(self, reason): err = ValidationError( _('%(reason)s is an invalid reason'), diff --git a/flag/models.py b/flag/models.py index 771a713..6cc3298 100644 --- a/flag/models.py +++ b/flag/models.py @@ -69,6 +69,12 @@ def get_clean_state(self, state): raise err return state + def get_verbose_state(self, state): + state = self.get_clean_state(state) + for item in self.STATE_CHOICES: + if item[0] == state: + return item[1] + def toggle_state(self, state, moderator): state = self.get_clean_state(state) # toggle states occurs between rejected and resolved states only diff --git a/flag/templatetags/flag_tags.py b/flag/templatetags/flag_tags.py index 4eb62dc..6cd069b 100644 --- a/flag/templatetags/flag_tags.py +++ b/flag/templatetags/flag_tags.py @@ -2,7 +2,7 @@ from django.core.exceptions import ImproperlyConfigured from django.utils.translation import gettext_lazy as _ -from flag.models import FlagInstance +from flag.models import Flag, FlagInstance from flag.conf import settings register = template.Library() @@ -18,7 +18,7 @@ def get_model_name(obj): def has_flagged(user, obj): if user.is_authenticated: - return FlagInstance.objects.has_flagged(user, obj) + return Flag.objects.has_flagged(user, obj) return False diff --git a/tests/test_api.py b/tests/test_api.py index fa5942f..8478647 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,5 +1,6 @@ from rest_framework import status +from flag.api.serializers import FlagSerializer from tests.base import BaseFlagAPITest, Flag, FlagInstance @@ -8,30 +9,38 @@ class FlagAPIViewsTest(BaseFlagAPITest): def setUpTestData(cls): super().setUpTestData() cls.post = cls.create_post() + cls.flag = Flag.objects.get_flag(cls.post) def setUp(self): super().setUp() self.url = '/api/flag/' + self.init_count = 0 + self.context = { + 'model_obj': self.post, + 'user': self.user_1 + } def test_flagging_successfully_with_url_encoded_form(self): data = self.data.copy() post = self.post data['model_id'] = post.id + self.flag.refresh_from_db() + init_count = self.flag.count + response = self.client.post(self.url, data=data) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual( - response.json()['detail'], - 'The content has been flagged successfully. A moderator will review it shortly.' - ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.flag.refresh_from_db() + self.assertEqual(response.data, FlagSerializer(self.flag, context=self.context).data) # check database - flag = Flag.objects.get_flag(post) __, created = FlagInstance.objects.get_or_create( - flag=flag, + flag=self.flag, user=response.wsgi_request.user, reason=data['reason'] ) self.assertEqual(created, False) + self.assertEqual(self.flag.count, init_count + 1) def test_flagging_successfully_with_json_format(self): data = self.data.copy() @@ -39,20 +48,18 @@ def test_flagging_successfully_with_json_format(self): data['model_id'] = post.id response = self.client.post(self.url, data=data, format='json') - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual( - response.json()['detail'], - 'The content has been flagged successfully. A moderator will review it shortly.' - ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.flag.refresh_from_db() + self.assertEqual(response.data, FlagSerializer(self.flag, context=self.context).data) # check database - flag = Flag.objects.get_flag(post) __, created = FlagInstance.objects.get_or_create( - flag=flag, + flag=self.flag, user=response.wsgi_request.user, reason=data['reason'] ) self.assertEqual(created, False) + self.assertEqual(self.flag.count, self.init_count + 1) def test_flagging_flagged_object(self): data = self.data.copy() @@ -83,18 +90,24 @@ def test_flagging_unflagged_object(self): def test_unflagging_successfully(self): # un-flag => no reason is passed and the content must be already flagged by the user post = self.post - self.set_flag(model_obj=post) + self.flag.refresh_from_db() + init_count = self.flag.count + self.set_flag(post) + self.flag.refresh_from_db() + self.assertEqual(self.flag.count, init_count + 1) + data = self.data.copy() data['model_id'] = post.id data.pop('reason') response = self.client.post(self.url, data=data) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.json()['detail'], 'The content has been unflagged successfully.') + self.flag.refresh_from_db() + self.assertEqual(response.data, FlagSerializer(self.flag, context=self.context).data) # check database - flag = Flag.objects.get_flag(post) + self.assertEqual(self.flag.count, init_count) __, created = FlagInstance.objects.get_or_create( - flag=flag, + flag=self.flag, user=response.wsgi_request.user, reason=FlagInstance.reason_values[0] ) @@ -132,15 +145,18 @@ def test_choosing_last_reason_with_info(self): """Test response when last reason is passed with info""" data = self.data.copy() post = self.post_2 + flag = Flag.objects.get_flag(post) + init_count = flag.count reason = FlagInstance.reason_values[-1] info = 'weird' data.update({'reason': reason, 'info': info, 'model_id': post.id}) response = self.client.post(self.url, data=data) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual( - response.json()['detail'], - 'The content has been flagged successfully. A moderator will review it shortly.' - ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + flag.refresh_from_db() + context = self.context.copy() + context['model_obj'] = post + self.assertEqual(response.data, FlagSerializer(flag, context=context).data) # check database - flag = Flag.objects.get_flag(post) self.assertEqual(FlagInstance.objects.get(user=response.wsgi_request.user, flag=flag).info, info) + self.assertEqual(flag.count, init_count + 1) diff --git a/tests/test_models.py b/tests/test_models.py index b571d06..19bec23 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -37,6 +37,29 @@ def test_get_clean_state(self): # None self.assertRaises(ValidationError, flag.get_clean_state, None) + def test_is_flagged(self): + post = self.post_1 + flag = Flag.objects.get_flag(post) + flag.count = 0 + flag.save() + + self.assertEqual(False, flag.is_flagged) + + self.set_flag(post) + flag.refresh_from_db() + self.assertEqual(True, flag.is_flagged) + + @patch('flag.models.Flag.get_clean_state') + def test_get_verbose_state(self, mocked_get_clean_state): + flag = self.flag + state = flag.State.FLAGGED + mocked_get_clean_state.return_value = state + + self.assertEqual(flag.get_verbose_state(state), flag.STATE_CHOICES[state-1][1]) + + mocked_get_clean_state.return_value = 100 + self.assertIsNone(flag.get_verbose_state(100)) + def test_toggle_state(self): post = self.content_object_2 flag = self.create_flag(post) @@ -88,6 +111,26 @@ def setUp(self): def test_get_flag(self): self.assertEqual(Flag.objects.get_flag(self.post_2), self.flag) + def test_has_flagged(self): + user = self.user_2 + post = self.post_2 + self.assertEqual(Flag.objects.has_flagged(user, post), False) + + self.set_flag(self.post_2, user=user) + + self.assertEqual(Flag.objects.has_flagged(user, post), True) + + def test_is_flagged(self): + post = self.post_1 + flag = Flag.objects.get_flag(post) + flag.count = 0 + flag.save() + + self.assertEqual(False, Flag.objects.is_flagged(post)) + + self.flag.increase_count() + self.assertEqual(False, Flag.objects.is_flagged(post)) + class FlagInstanceModelTest(BaseFlagModelTest): def test_create_flag_instance(self): @@ -101,16 +144,6 @@ def test_create_flag_instance(self): class TestFlagInstanceManager(BaseFlagModelTest): - - def test_has_flagged(self): - user = self.user_2 - post = self.post_2 - self.assertEqual(FlagInstance.objects.has_flagged(user, post), False) - - self.set_flag(self.post_2, user=user) - - self.assertEqual(FlagInstance.objects.has_flagged(user, post), True) - def test_clean_when_last_reason_is_used(self): flag = self.flag