From 65aac3daf115ac755ff8c0f6893906aefd6a17f0 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Sun, 18 Nov 2018 14:36:15 +0200 Subject: [PATCH 1/5] Add LimitOffsetPagination --- sikweb/base.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sikweb/base.py b/sikweb/base.py index bbb197c..460f759 100644 --- a/sikweb/base.py +++ b/sikweb/base.py @@ -232,6 +232,8 @@ REST_FRAMEWORK = { 'burst': '60/min', 'sustained': '1000/day' }, + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', + 'PAGE_SIZE': 10, } # Email settings (tested working with gmail) From 55fdf8f60ded471bda517814cf4b958acf2e79d8 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Sun, 18 Nov 2018 15:29:51 +0200 Subject: [PATCH 2/5] Add filtering for Event and Signup ViewSets --- requirements.txt | 3 ++- sikweb/base.py | 4 ++++ webapp/views.py | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8d18fcf..4865fcd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -35,4 +35,5 @@ pyexcel==0.5.8 pyexcel-xlsx==0.5.5 django-import-export==0.7.0 openpyxl==2.4.11 -django-app-namespace-template-loader==0.4.1 \ No newline at end of file +django-app-namespace-template-loader==0.4.1 +django-filter==2.0.0 \ No newline at end of file diff --git a/sikweb/base.py b/sikweb/base.py index 460f759..1619797 100644 --- a/sikweb/base.py +++ b/sikweb/base.py @@ -103,6 +103,7 @@ INSTALLED_APPS = [ 'auditlog', 'phonenumber_field', 'import_export', + 'django_filters', ] IMPORT_EXPORT_USE_TRANSACTIONS = True @@ -234,6 +235,9 @@ REST_FRAMEWORK = { }, 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 10, + 'DEFAULT_FILTER_BACKENDS': ( + 'django_filters.rest_framework.DjangoFilterBackend', + ), } # Email settings (tested working with gmail) diff --git a/webapp/views.py b/webapp/views.py index 6f9c02b..04cdf71 100644 --- a/webapp/views.py +++ b/webapp/views.py @@ -13,6 +13,8 @@ from rest_framework import viewsets, routers from rest_framework.permissions import IsAuthenticatedOrReadOnly from rest_framework.response import Response from rest_framework.reverse import reverse +from django_filters import rest_framework as filters +from rest_framework.filters import SearchFilter, OrderingFilter # import logging # import requests @@ -33,18 +35,27 @@ class EventViewSet(viewsets.ModelViewSet): queryset = Event.objects.all() serializer_class = EventSerializer permission_classes = [IsAuthenticatedOrReadOnly] + filter_backends = (filters.DjangoFilterBackend, SearchFilter, OrderingFilter) + filter_fields = '__all__' + search_fields = '__all__' class SignupFormViewSet(viewsets.ModelViewSet): queryset = SignupForm.objects.all() serializer_class = SignupFormSerializer permission_classes = [IsAuthenticatedOrReadOnly] + filter_backends = (filters.DjangoFilterBackend, SearchFilter, OrderingFilter) + filter_fields = '__all__' + search_fields = '__all__' class SignupViewSet(viewsets.ModelViewSet): queryset = Signup.objects.all() serializer_class = SignupSerializer permission_classes = [] + filter_backends = (filters.DjangoFilterBackend, SearchFilter, OrderingFilter) + filter_fields = '__all__' + search_fields = '__all__' class SavedQuestionsViewSet(viewsets.ModelViewSet): @@ -57,6 +68,9 @@ class FeedViewSet(viewsets.ModelViewSet): queryset = Feed.objects.all() serializer_class = FeedSerializer permission_classes = [] + filter_backends = (filters.DjangoFilterBackend, SearchFilter, OrderingFilter) + filter_fields = '__all__' + search_fields = '__all__' class ContactsViewSet(viewsets.ReadOnlyModelViewSet): From e1220d17bb5d447beb977c676030b657a35eb7d6 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Sun, 18 Nov 2018 17:54:06 +0200 Subject: [PATCH 3/5] Add filtering to REST API --- webapp/migrations/0050_signupform_visible.py | 18 +++++++++++ webapp/models.py | 1 + webapp/views.py | 32 ++++++++++++++++---- 3 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 webapp/migrations/0050_signupform_visible.py diff --git a/webapp/migrations/0050_signupform_visible.py b/webapp/migrations/0050_signupform_visible.py new file mode 100644 index 0000000..5e666ab --- /dev/null +++ b/webapp/migrations/0050_signupform_visible.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.7 on 2018-11-18 15:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('webapp', '0049_auto_20181118_1344'), + ] + + operations = [ + migrations.AddField( + model_name='signupform', + name='visible', + field=models.BooleanField(default=True), + ), + ] diff --git a/webapp/models.py b/webapp/models.py index 856d00a..716942c 100644 --- a/webapp/models.py +++ b/webapp/models.py @@ -93,6 +93,7 @@ class SignupForm(models.Model): end = models.DateTimeField(default=timezone.now) # question = JSONField() questions = models.CharField(max_length=255) + visible = models.BooleanField(default=True) class Meta: verbose_name = _('Signup form') diff --git a/webapp/views.py b/webapp/views.py index 04cdf71..1b70e31 100644 --- a/webapp/views.py +++ b/webapp/views.py @@ -15,7 +15,7 @@ from rest_framework.response import Response from rest_framework.reverse import reverse from django_filters import rest_framework as filters from rest_framework.filters import SearchFilter, OrderingFilter - +from rest_framework import permissions # import logging # import requests from dealer.git import git @@ -26,6 +26,14 @@ from webapp.serializers import * from members.views.utils import * +class IsPostOrIsAuthenticated(permissions.BasePermission): + + def has_permission(self, request, view): + if request.method == 'POST': + return True + return request.user and request.user.is_authenticated + + # -- REST API -- # class RootView(routers.APIRootView): permission_classes = [IsAuthenticatedOrReadOnly] @@ -39,6 +47,9 @@ class EventViewSet(viewsets.ModelViewSet): filter_fields = '__all__' search_fields = '__all__' + def get_queryset(self): + return Event.objects.filter(visible=True, end_time__gt=timezone.now()).order_by('start_time') + class SignupFormViewSet(viewsets.ModelViewSet): queryset = SignupForm.objects.all() @@ -48,14 +59,20 @@ class SignupFormViewSet(viewsets.ModelViewSet): filter_fields = '__all__' search_fields = '__all__' + def get_queryset(self): + return SignupForm.objects.filter(visible=True, end__gt=timezone.now()).order_by('start') + class SignupViewSet(viewsets.ModelViewSet): queryset = Signup.objects.all() serializer_class = SignupSerializer - permission_classes = [] - filter_backends = (filters.DjangoFilterBackend, SearchFilter, OrderingFilter) - filter_fields = '__all__' - search_fields = '__all__' + permission_classes = [IsPostOrIsAuthenticated] + # filter_backends = (filters.DjangoFilterBackend, SearchFilter, OrderingFilter) + # filter_fields = '__all__' + # search_fields = '__all__' + + # def get_queryset(self): + # return Signup.objects.filter(visible=True, end_time__gt=timezone.now()).order_by('start_time') class SavedQuestionsViewSet(viewsets.ModelViewSet): @@ -67,11 +84,14 @@ class SavedQuestionsViewSet(viewsets.ModelViewSet): class FeedViewSet(viewsets.ModelViewSet): queryset = Feed.objects.all() serializer_class = FeedSerializer - permission_classes = [] + permission_classes = [IsAuthenticatedOrReadOnly] filter_backends = (filters.DjangoFilterBackend, SearchFilter, OrderingFilter) filter_fields = '__all__' search_fields = '__all__' + def get_queryset(self): + return Feed.objects.filter(visible=True, autohide__gt=timezone.now()).order_by('publish_time') + class ContactsViewSet(viewsets.ReadOnlyModelViewSet): queryset = Official.objects.all() From d3e60138401ebc75fde5562c1f5f3028abd9bb02 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Sun, 18 Nov 2018 18:12:41 +0200 Subject: [PATCH 4/5] Fix API test by getting results from json --- webapp/tests.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/tests.py b/webapp/tests.py index 1d0c016..68d2a43 100644 --- a/webapp/tests.py +++ b/webapp/tests.py @@ -24,7 +24,7 @@ class TagsTestCase(APITestCase): self.assertTrue(status.is_success(response.status_code)) # We dont care about icon, so response is sliced - sliced_response = OrderedDict(islice(response.data[0].items(), 3)) + sliced_response = OrderedDict(islice(response.data['results'][0].items(), 3)) tag1 = Tag.objects.get(slug="Party") self.assertEqual(sliced_response, {'id': tag1.id, 'slug': 'Party', 'name': 'Bileet'}) @@ -39,12 +39,12 @@ class TagsTestCase(APITestCase): # We dont care about icon, so response is sliced tag1 = Tag.objects.get(slug="Party") - sliced_response = OrderedDict(islice(response.data[0].items(), 3)) + sliced_response = OrderedDict(islice(response.data['results'][0].items(), 3)) self.assertEqual(sliced_response, {'id': tag1.id, 'slug': 'Party', 'name': 'Bileet'}) - sliced_response = OrderedDict(islice(response.data[1].items(), 3)) + sliced_response = OrderedDict(islice(response.data['results'][1].items(), 3)) tag2 = Tag.objects.get(slug="Freshmen") self.assertEqual(sliced_response, {'id': tag2.id, 'slug': 'Freshmen', 'name': 'Fuksit'}) - sliced_response = OrderedDict(islice(response.data[2].items(), 3)) + sliced_response = OrderedDict(islice(response.data['results'][2].items(), 3)) tag3 = Tag.objects.get(slug="International") self.assertEqual(sliced_response, {'id': tag3.id, 'slug': 'International', 'name': 'Ulkkarit'}) @@ -83,7 +83,7 @@ class FeedTestCase(APITestCase): feeds = Feed.objects.all() serializer = FeedSerializer(feeds, many=True) - self.assertEqual(response.data, serializer.data) + self.assertEqual(response.data['results'], serializer.data) def test_post_feed(self): Tag.objects.create(slug="test1", name="testsds") From 1ecf3c368be36425f837d3ca172b2597d4655e4d Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Sun, 18 Nov 2018 18:58:12 +0200 Subject: [PATCH 5/5] Add authentication for post test --- webapp/tests.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/webapp/tests.py b/webapp/tests.py index 68d2a43..7879e70 100644 --- a/webapp/tests.py +++ b/webapp/tests.py @@ -2,8 +2,10 @@ from django.test import TestCase from django.core.files import File +from django.contrib.auth.models import User from rest_framework.test import APITestCase from rest_framework import status +from rest_framework.test import force_authenticate from webapp.models import Tag, Feed from webapp.serializers import TagSerializer, FeedSerializer @@ -76,6 +78,9 @@ class FeedTestCase(APITestCase): self.assertEqual(Feed.objects.count(), 1) self.assertEqual(Feed.objects.all()[0].tags.count(), 2) + username, password = 'test_admin', 'password123' + self.authClient = User.objects.create_superuser(username, 'myemail@test.com', password) + def test_get_feed(self): response = self.client.get('/api/feed/', format='json') self.assertTrue(status.is_success(response.status_code)) @@ -92,9 +97,15 @@ class FeedTestCase(APITestCase): tag2_id = Tag.objects.get(slug="test2").id data = {'tags': [tag1_id, tag2_id], 'title': 'testtitle', 'visible': 'True', 'description': 'liirumlaarum', 'content': 'lorem ipsum'} + # Try post without authentication response = self.client.post('/api/feed/', data, format='multipart') + self.assertTrue(status.is_client_error(response.status_code)) + self.assertEqual(Feed.objects.count(), 1) + # Authenticate + self.client.force_authenticate(user=self.authClient) + response = self.client.post('/api/feed/', data, format='multipart') + # Return success and check object was created self.assertTrue(status.is_success(response.status_code)) - self.assertEqual(Feed.objects.count(), 2) created = Feed.objects.get(title="testtitle")