From 38669947cdab506c93863b0b45c8c8f6130de60e Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Tue, 4 Aug 2020 01:04:38 +0300 Subject: [PATCH] Some serialization BS --- sikweb/base.py | 4 +--- webapp/models.py | 5 ++++ webapp/serializers.py | 36 +++++++++++++++------------- webapp/tests/test_event.py | 14 +++++++---- webapp/tests/test_signup.py | 7 ++++++ webapp/tests/test_signup_errors.py | 15 ++++++++++-- webapp/views.py | 38 ++++++++++++++++++++---------- 7 files changed, 80 insertions(+), 39 deletions(-) diff --git a/sikweb/base.py b/sikweb/base.py index 5caee47..8465dfc 100644 --- a/sikweb/base.py +++ b/sikweb/base.py @@ -127,9 +127,7 @@ MIDDLEWARE = [ 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'auditlog.middleware.AuditlogMiddleware' ] -MIDDLEWARE_CLASSES = [ - 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', -] + CORS_ORIGIN_ALLOW_ALL = True ROOT_URLCONF = 'sikweb.urls' diff --git a/webapp/models.py b/webapp/models.py index 0fefb96..731d652 100644 --- a/webapp/models.py +++ b/webapp/models.py @@ -108,6 +108,11 @@ class SignupForm(models.Model): def signups(self): return Signup.objects.filter(signupForm=self).order_by('pk') + @property + def isOpen(self): + now = timezone.now() + return self.start_time <= now and now < self.end_time + class Meta: verbose_name = _('Signup form') verbose_name_plural = _('Signup forms') diff --git a/webapp/serializers.py b/webapp/serializers.py index e8789a2..0a1049e 100644 --- a/webapp/serializers.py +++ b/webapp/serializers.py @@ -7,7 +7,6 @@ class SignupSerializer(serializers.ModelSerializer): source="signupForm", queryset=SignupForm.objects.all() ) - answer = serializers.JSONField() list_name = serializers.CharField(read_only=True) def add_extra_fields(self, validated_data): @@ -44,8 +43,7 @@ class SignupSerializer(serializers.ModelSerializer): } -class SignupFormSerializer(serializers.HyperlinkedModelSerializer): - questions = serializers.JSONField() +class SignupFormSerializer(serializers.ModelSerializer): signups = serializers.SlugRelatedField( slug_field="list_name", many=True, @@ -55,36 +53,37 @@ class SignupFormSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = SignupForm - fields = ('id', 'title', 'visible', 'start_time', 'end_time', 'questions', 'schema', 'signups', 'quota') + fields = ('id', 'title', 'visible', 'isOpen', 'start_time', 'end_time', 'questions', 'schema', 'signups', 'quota') class EventSerializer(serializers.ModelSerializer): signupForm = SignupFormSerializer( + source='filtered_signup_forms', many=True, read_only=True, - required=False, ) + signup_id = serializers.PrimaryKeyRelatedField( - many=True, - source="signupForm", queryset=SignupForm.objects.all(), - required=False + many=True, + write_only=True, ) tag_id = serializers.PrimaryKeyRelatedField( + queryset=Tag.objects.all(), many=True, - source="tags", - queryset=Tag.objects.all() + write_only=True, ) class Meta: model = Event fields = ('id', 'tag_id', 'tags', 'visible', 'image', 'title_fi', 'title_en', 'description_fi', 'description_en', 'content_fi', 'content_en', 'start_time', 'end_time', 'location', 'signup_id', 'signupForm') + read_only_fields = ['tags', 'signupForm'] depth = 1 def create(self, validated_data): - signupForms = validated_data.pop('signupForm', []) - tags = validated_data.pop('tags') + signupForms = validated_data.pop('signup_id', []) + tags = validated_data.pop('tag_id') event = Event.objects.create(**validated_data) for form in signupForms: event.signupForm.add(form) @@ -94,12 +93,15 @@ class EventSerializer(serializers.ModelSerializer): return event def update(self, instance, validated_data): - signupForms = validated_data.pop('signupForm', []) - instance = super(EventSerializer, self).update(instance, validated_data) + signupForms = validated_data.pop('signup_id', []) + tags = validated_data.pop('tag_id') instance.signupForm.clear() - for form_data in signupForms: - # form_qs = SignupForms.objects.filter(id=form['id']) - instance.signupForm.add(form_data) + instance.tags.clear() + for form in signupForms: + instance.signupForm.add(form) + for tag in tags: + instance.tags.add(tag) + instance = super(EventSerializer, self).update(instance, validated_data) return instance diff --git a/webapp/tests/test_event.py b/webapp/tests/test_event.py index b6f49b3..4c0af53 100644 --- a/webapp/tests/test_event.py +++ b/webapp/tests/test_event.py @@ -1,4 +1,4 @@ -from django.contrib.auth.models import User +from django.contrib.auth.models import User, AnonymousUser from django.utils import timezone from rest_framework import status from rest_framework.test import APITestCase, APIRequestFactory @@ -57,11 +57,13 @@ class EventTestCase(APITestCase): # Response should not have old events and invisible self.assertEqual(len(response.data["results"]), 2) # Check that serialized data is equal to received response + req = APIRequestFactory().get(r"http://testserver/api/events/") + req.user = AnonymousUser() expected_events = EventSerializer( Event.objects.filter(title_fi__in=("Testitapahtuma1", "Testitapahtuma4")).order_by("start_time"), many=True, context={ - "request": APIRequestFactory().get(r"http://testserver/api/events/") + "request": req } ) self.assertEqual(response.data["results"], expected_events.data) @@ -71,11 +73,13 @@ class EventTestCase(APITestCase): self.assertTrue(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.data["results"]), 3) + req = APIRequestFactory().get(r"http://testserver/api/events/") + req.user = AnonymousUser() expected_events = EventSerializer( Event.objects.filter(title_fi__in=("Testitapahtuma1", "Testitapahtuma3", "Testitapahtuma4")).order_by("start_time"), many=True, context={ - "request": APIRequestFactory().get(r"http://testserver/api/events/") + "request": req } ) self.assertEqual(response.data["results"], expected_events.data) @@ -84,10 +88,12 @@ class EventTestCase(APITestCase): response = self.client.get(f"{URL}{self.testEventId}/", format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) + req = APIRequestFactory().get(r"http://testserver/api/events/") + req.user = AnonymousUser() serializer = EventSerializer( Event.objects.get(title_fi="Testitapahtuma1"), context={ - "request": APIRequestFactory().get(r"http://testserver/api/events/") + "request": req } ) self.assertEqual(response.data, serializer.data) diff --git a/webapp/tests/test_signup.py b/webapp/tests/test_signup.py index 4f0664e..4ec1ef8 100644 --- a/webapp/tests/test_signup.py +++ b/webapp/tests/test_signup.py @@ -49,6 +49,13 @@ class SignupTestCase(APITestCase): self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(Signup.objects.count(), 3) + # Can signup to a hidden form + def test_create_signup_hidden(self): + new = createSignupRequest("asd", self.hiddenForm.id, ALL_QUESTION_TYPES_ANSWER) + response = self.client.post(URL, new, format="json") + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(Signup.objects.count(), 3) + @skip("NotImplemented") def test_get_hidden_forms_admin(self): pass diff --git a/webapp/tests/test_signup_errors.py b/webapp/tests/test_signup_errors.py index d8c4483..e7a9c93 100644 --- a/webapp/tests/test_signup_errors.py +++ b/webapp/tests/test_signup_errors.py @@ -1,3 +1,4 @@ +from django.utils import timezone from django.contrib.auth.models import User from unittest import skip from rest_framework import status @@ -15,6 +16,10 @@ class SignupErrorTestCase(APITestCase): self.signupFormRadio = createSignupForm(name="Form3", questions=[ALL_QUESTION_TYPES[1]], schema=RADIO_SCHEMA) self.signupFormCheck = createSignupForm(name="Form4", questions=[ALL_QUESTION_TYPES[2]], schema=CBOX_SCHEMA) self.hiddenForm = createSignupForm(visible=False) + day_from_now = timezone.now() + timezone.timedelta(days=1) + day_before_now = timezone.now() + timezone.timedelta(days=-1) + self.signupFormNotStarted = createSignupForm(start_time=day_from_now, end_time=day_from_now) + self.signupFormEnded = createSignupForm(start_time=day_before_now, end_time=timezone.now()) self.signup1 = createSignupObject("1", self.signupForm, ALL_QUESTION_TYPES) self.signup2 = createSignupObject("2", self.signupForm, ALL_QUESTION_TYPES) @@ -37,8 +42,14 @@ class SignupErrorTestCase(APITestCase): self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(Signup.objects.count(), 2) - def test_create_signup_hidden(self): - new = createSignupRequest("asd", self.hiddenForm.id, ALL_QUESTION_TYPES_ANSWER) + def test_create_signup_not_started(self): + new = createSignupRequest("asd", self.signupFormNotStarted.id, ALL_QUESTION_TYPES_ANSWER) + response = self.client.post(URL, new, format="json") + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual(Signup.objects.count(), 2) + + def test_create_signup_ended(self): + new = createSignupRequest("asd", self.signupFormEnded.id, ALL_QUESTION_TYPES_ANSWER) response = self.client.post(URL, new, format="json") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(Signup.objects.count(), 2) diff --git a/webapp/views.py b/webapp/views.py index ffaf74b..166494a 100644 --- a/webapp/views.py +++ b/webapp/views.py @@ -9,8 +9,10 @@ from django.http import HttpResponse, JsonResponse from django.shortcuts import render, get_object_or_404 from django.views.decorators.http import require_http_methods from django_filters import rest_framework as filters +from django.db.models import Prefetch from django.core.exceptions import ObjectDoesNotExist -from rest_framework import routers, viewsets +from rest_framework import routers +from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet from rest_framework.filters import OrderingFilter, SearchFilter from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticatedOrReadOnly, BasePermission, AllowAny @@ -37,7 +39,7 @@ class RootView(routers.APIRootView): permission_classes = [IsAuthenticatedOrReadOnly] -class EventViewSet(viewsets.ModelViewSet): +class EventViewSet(ModelViewSet): queryset = Event.objects.all() ordering = ["start_time"] serializer_class = EventSerializer @@ -47,13 +49,23 @@ class EventViewSet(viewsets.ModelViewSet): # search_fields = '__all__' def get_queryset(self): - if self.request.user.is_authenticated: + + if self.request.method == 'POST' or self.request.method == 'PUT': return Event.objects.all() + if self.request.user.is_authenticated: + return Event.objects.all().prefetch_related( + Prefetch('signupForm', queryset=SignupForm.objects.all(), to_attr='filtered_signup_forms') + ) + since = self.request.query_params.get('since', None) if since: - return Event.objects.filter(visible=True, end_time__gt=since).order_by('start_time') - return Event.objects.filter(visible=True, end_time__gt=timezone.now()).order_by('start_time') + return Event.objects.filter(visible=True, end_time__gt=since).order_by('start_time').prefetch_related( + Prefetch('signupForm', queryset=SignupForm.objects.filter(visible=True), to_attr='filtered_signup_forms') + ) + return Event.objects.filter(visible=True, end_time__gt=timezone.now()).order_by('start_time').prefetch_related( + Prefetch('signupForm', queryset=SignupForm.objects.filter(visible=True), to_attr='filtered_signup_forms') + ) def create(self, request, *args, **kwargs): raw_image = request.data.get("image", None) @@ -74,7 +86,7 @@ class EventViewSet(viewsets.ModelViewSet): return super().update(request, *args, **kwargs) -class SignupFormViewSet(viewsets.ModelViewSet): +class SignupFormViewSet(ModelViewSet): queryset = SignupForm.objects.all() serializer_class = SignupFormSerializer permission_classes = [IsAuthenticatedOrReadOnly] @@ -95,7 +107,7 @@ class SignupFormViewSet(viewsets.ModelViewSet): return SignupForm.objects.filter(visible=True, end_time__gt=timezone.now()).order_by('start_time') -class SignupViewSet(viewsets.ModelViewSet): +class SignupViewSet(ModelViewSet): queryset = Signup.objects.all() serializer_class = SignupSerializer permission_classes = [SignupPermission] @@ -116,7 +128,7 @@ class SignupViewSet(viewsets.ModelViewSet): try: answer = request.data["answer"] form = SignupForm.objects.get(id=id) - if (form.visible): + if (form.isOpen): # Throws ValidationError if not valid validate(instance=answer, schema=form.schema) return super().create(request, *args, **kwargs) @@ -147,13 +159,13 @@ class SignupViewSet(viewsets.ModelViewSet): return JsonResponse(status=404, data={"error": f"SignupForm {id} not found"}) -class SavedQuestionsViewSet(viewsets.ModelViewSet): +class SavedQuestionsViewSet(ModelViewSet): queryset = TemplateQuestion.objects.all() serializer_class = SavedQuestionsSerializer permission_classes = [IsAuthenticatedOrReadOnly] -class FeedViewSet(viewsets.ModelViewSet): +class FeedViewSet(ModelViewSet): queryset = Feed.objects.all() serializer_class = FeedSerializer permission_classes = [IsAuthenticatedOrReadOnly] @@ -178,7 +190,7 @@ class FeedViewSet(viewsets.ModelViewSet): return Feed.objects.filter(id__in=result_ids) -class ContactsViewSet(viewsets.ReadOnlyModelViewSet): +class ContactsViewSet(ReadOnlyModelViewSet): queryset = Occupation.objects.all() serializer_class = OccupationSerializer permission_classes = [IsAuthenticatedOrReadOnly] @@ -190,13 +202,13 @@ class ContactsViewSet(viewsets.ReadOnlyModelViewSet): return Occupation.by_year(int(year)) -class CommitteeViewSet(viewsets.ReadOnlyModelViewSet): +class CommitteeViewSet(ReadOnlyModelViewSet): queryset = Committee.objects.all() serializer_class = CommitteeSerializer permission_classes = [IsAuthenticatedOrReadOnly] -class TagsViewSet(viewsets.ReadOnlyModelViewSet): +class TagsViewSet(ReadOnlyModelViewSet): queryset = Tag.objects.all() serializer_class = TagSerializer permission_classes = [IsAuthenticatedOrReadOnly]