From ea62c3d1c40f7e9d388cbb430be67a39b3af08f7 Mon Sep 17 00:00:00 2001 From: reiosantos Date: Thu, 13 Sep 2018 19:37:11 +0300 Subject: [PATCH] feature(paginate articles): Pagination support for articles. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Enable users to paginate articles [Finishes  #159952015] Co-authored-by: huxaiphaer Co-authored-by: reiosantos --- .../apps/articles/migrations/0001_initial.py | 7 ++++- .../migrations/0002_article_author.py | 22 ------------- authors/apps/articles/renderer.py | 10 +++--- authors/apps/articles/serializers.py | 31 ++++++++++++++++++- authors/apps/articles/tests/test_articles.py | 10 +++--- authors/apps/articles/views.py | 12 ++++--- 6 files changed, 53 insertions(+), 39 deletions(-) delete mode 100644 authors/apps/articles/migrations/0002_article_author.py diff --git a/authors/apps/articles/migrations/0001_initial.py b/authors/apps/articles/migrations/0001_initial.py index ea98043..5ff2251 100644 --- a/authors/apps/articles/migrations/0001_initial.py +++ b/authors/apps/articles/migrations/0001_initial.py @@ -1,13 +1,17 @@ -# Generated by Django 2.1 on 2018-09-13 09:42 +# Generated by Django 2.1 on 2018-09-14 07:51 +from django.conf import settings from django.db import migrations, models +import django.db.models.deletion import django.utils.timezone class Migration(migrations.Migration): + initial = True dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ @@ -26,6 +30,7 @@ class Migration(migrations.Migration): ('favorited', models.BooleanField(default=False)), ('favorites_count', models.IntegerField(default=0)), ('photo_url', models.CharField(max_length=255, null=True)), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], options={ 'ordering': ['-created_at', 'author'], diff --git a/authors/apps/articles/migrations/0002_article_author.py b/authors/apps/articles/migrations/0002_article_author.py deleted file mode 100644 index 3fe2df0..0000000 --- a/authors/apps/articles/migrations/0002_article_author.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 2.1 on 2018-09-13 09:42 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('articles', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='article', - name='author', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/authors/apps/articles/renderer.py b/authors/apps/articles/renderer.py index 7bac594..aa3397a 100644 --- a/authors/apps/articles/renderer.py +++ b/authors/apps/articles/renderer.py @@ -20,16 +20,16 @@ def render(self, data, accepted_media_type=None, renderer_context=None): :param renderer_context: :return: """ - if isinstance(data, list) and len(data) > 1: + val = data.get("results", None) + if val and isinstance(val, list) and len(val) > 1: return json.dumps({ 'articles': data }) - if isinstance(data, list) and len(data) is 1: - data = data[0] + if val and isinstance(val, list) and len(val) is 1: + val = val[0] - if isinstance(data, list) and len(data) < 1: - data = {} + data.update({"results": val}) errors = data.get('errors', None) diff --git a/authors/apps/articles/serializers.py b/authors/apps/articles/serializers.py index fa57688..1d02531 100644 --- a/authors/apps/articles/serializers.py +++ b/authors/apps/articles/serializers.py @@ -2,12 +2,15 @@ Serializer classes for articles """ from rest_framework import serializers +from rest_framework.pagination import PageNumberPagination from authors.apps.articles.exceptions import NotFoundException from authors.apps.articles.models import Article from authors.apps.articles.utils import get_date from authors.apps.authentication.models import User from authors.apps.authentication.serializers import UserSerializer +from authors.apps.profiles.models import UserProfile +from authors.apps.profiles.serializers import RetrieveUserProfileSerializer class ArticleSerializer(serializers.ModelSerializer): @@ -64,7 +67,9 @@ def to_representation(self, instance): :return: """ response = super().to_representation(instance) - response['author'] = UserSerializer(instance.author).data + profile = RetrieveUserProfileSerializer(UserProfile.objects.get(user=instance.author)).data + + response['author'] = profile return response class Meta: @@ -75,3 +80,27 @@ class behaviours # noinspection SpellCheckingInspection fields = ('slug', 'title', 'description', 'body', 'created_at', 'updated_at', 'favorited', 'favorites_count', 'photo_url', 'author') + + +class PaginatedArticleSerializer(PageNumberPagination): + """ + Pagination class + Inherits from PageNumberPagination + Paginates articles + """ + page_size = 4 + + def get_paginated_response(self, data): + """ + Formats response to include page links + :param data: + :return: + """ + return { + 'links': { + 'next': self.get_next_link(), + 'previous': self.get_previous_link() + }, + 'count': self.page.paginator.count, + 'results': data + } diff --git a/authors/apps/articles/tests/test_articles.py b/authors/apps/articles/tests/test_articles.py index d99325e..0dcc8fe 100644 --- a/authors/apps/articles/tests/test_articles.py +++ b/authors/apps/articles/tests/test_articles.py @@ -115,7 +115,7 @@ def test_list_specific_article(self): self.assertIn('article', response.json()) self.assertIsInstance(response.json().get("article"), dict) - slug = response.json().get("article").get("slug") + slug = response.json().get("article").get("results").get("slug") response = self.client.get( "/api/articles/{0}/".format(slug), content_type='application/json') @@ -137,7 +137,7 @@ def test_delete_specific_article(self): self.assertIn('article', response.json()) self.assertIsInstance(response.json().get("article"), dict) - slug = response.json().get("article").get("slug") + slug = response.json().get("article").get("results").get("slug") response = self.client.delete( "/api/articles/{0}/".format(slug), content_type='application/json') @@ -166,7 +166,7 @@ def test_list_many_articles(self): response = self.client.get( "/api/articles/", content_type='application/json') self.assertEqual(response.status_code, 200) - self.assertIsInstance(response.json().get("articles"), list) + self.assertIsInstance(response.json().get("articles").get("results"), list) self.assertEqual(len(response.json().get("articles")), 3) def test_update_article(self): @@ -183,7 +183,7 @@ def test_update_article(self): self.assertIn('article', response.json()) self.assertIsInstance(response.json().get("article"), dict) - slug = response.json().get("article").get("slug") + slug = response.json().get("article").get("results").get("slug") response = self.client.put( "/api/articles/{0}/".format(slug), data=json.dumps( @@ -206,7 +206,7 @@ def test_update_article_missing_data_and_not_exist(self): self.assertIn('article', response.json()) self.assertIsInstance(response.json().get("article"), dict) - slug = response.json().get("article").get("slug") + slug = response.json().get("article").get("results").get("slug") response = self.client.put( "/api/articles/{0}/".format(slug), data=json.dumps( diff --git a/authors/apps/articles/views.py b/authors/apps/articles/views.py index 873a63f..8b73cbe 100644 --- a/authors/apps/articles/views.py +++ b/authors/apps/articles/views.py @@ -10,7 +10,7 @@ from authors.apps.articles.exceptions import NotFoundException, InvalidQueryParameterException from authors.apps.articles.models import Article from authors.apps.articles.renderer import ArticleJSONRenderer -from authors.apps.articles.serializers import ArticleSerializer +from authors.apps.articles.serializers import ArticleSerializer, PaginatedArticleSerializer # noinspection PyUnusedLocal,PyMethodMayBeStatic @@ -51,12 +51,14 @@ def to_int(val): queryset = Article.objects.all() if queryset.count() > 0: - queryset = queryset[offset:limit] + queryset = queryset[offset:] - serializer = self.serializer_class(queryset, many=True) - data = serializer.data + data = self.serializer_class(queryset, many=True).data - return Response(data) + pager_class = PaginatedArticleSerializer() + pager_class.page_size = limit + + return Response(pager_class.get_paginated_response(pager_class.paginate_queryset(data, request))) def retrieve(self, request, slug=None): """