Skip to content

Commit

Permalink
feat(filter-search-functionality): implement filter and search functi…
Browse files Browse the repository at this point in the history
…onality of articles

 - ensure users can filter articles based on author's username, article title, tags
 - ensure users can search fo articles based on keywords

[Starts #163383192]
  • Loading branch information
oma0256 committed Feb 14, 2019
1 parent 791c72f commit b8dff4c
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 6 deletions.
152 changes: 152 additions & 0 deletions authors/apps/articles/tests/test_filter_search_functionality.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
from django.urls import reverse
from rest_framework import status
from ...authentication.tests.base_class import BaseTest


class TestFilterSearchFunctionality(BaseTest):
"""
TestFilterSearchFunctionality handles testing of the search and filter
functionality of articles
"""
def setUp(self):
super().setUp()
self.user = self.activated_user()
self.client.force_authenticate(user=self.user)
self.article = self.create_article(self.user)
self.url = reverse('articles:articles')

def add_tags_to_article(self):
"""
add_tags_to_article method adds a tag to an article
"""
self.article.tag_list = ["okay"]
self.article.save()

def test_user_can_filter_articles_by_author_username(self):
"""
Test user can filter articles based on author username
"""
username = self.user.username
response = self.client.get(f"{self.url}?author={username}")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 1)
response = self.client.get(f"{self.url}?author=vdhfvsf")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 0)

def test_user_can_filter_articles_by_title(self):
"""
Test user can filter articles based on article title
"""
title = self.article.title
response = self.client.get(f"{self.url}?title={title}")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 1)
response = self.client.get(f"{self.url}?title=vdhfvsf")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 0)

def test_user_can_filter_articles_by_tag(self):
"""
Test user can filter articles based on article tag
"""
self.add_tags_to_article()
tag = self.article.tag_list[0]
response = self.client.get(f"{self.url}?tag={tag}")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 1)
response = self.client.get(f"{self.url}?tag=vdhfvsf")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 0)

def test_user_can_filter_by_author_username_and_article_title(self):
"""
Test user can filer for articles based on author username and article
title
"""
username = self.user.username
title = self.article.title
response = self.client.get(
f"{self.url}?author={username}&title={title}"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 1)
response = self.client.get(f"{self.url}?title=vfvsf&author={username}")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 0)
response = self.client.get(f"{self.url}?title={title}&author=skjfg")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 0)

def test_user_can_filter_by_author_username_and_tag(self):
"""
Test user can filer for articles based on author username and tag
"""
self.add_tags_to_article()
username = self.user.username
tag = self.article.tag_list[0]
response = self.client.get(
f"{self.url}?author={username}&tag={tag}"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 1)
response = self.client.get(f"{self.url}?tag=vfvsf&author={username}")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 0)
response = self.client.get(f"{self.url}?tag={tag}&author=skjfg")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 0)

def test_user_can_filter_by_author_username_article_title_and_tag(self):
"""
Test user can filer for articles based on author username, article
title and tag
"""
self.add_tags_to_article()
username = self.user.username
title = self.article.title
tag = self.article.tag_list[0]
response = self.client.get(
f"{self.url}?author={username}&tag={tag}&title={title}"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 1)
response = self.client.get(
f"{self.url}?tag=vfvsf&author={username}&title={title}"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 0)
response = self.client.get(
f"{self.url}?tag={tag}&author=skjfg&title={title}"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 0)
response = self.client.get(
f"{self.url}?tag={tag}&author={username}&title=hjsdgfhj"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 0)

def test_user_can_search_for_articles_by_keywords(self):
"""
Test user can search for articles based on keywords
"""
username = self.user.username
title = self.article.title
description = self.article.description
body = self.article.body
response = self.client.get(f"{self.url}?search={username}")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 1)
response = self.client.get(f"{self.url}?search={title}")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 1)
response = self.client.get(f"{self.url}?search={description}")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 1)
response = self.client.get(f"{self.url}?search={body}")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 1)
response = self.client.get(f"{self.url}?search=vdhfvsf")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 0)
21 changes: 21 additions & 0 deletions authors/apps/articles/utils/custom_filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from django_filters import FilterSet, rest_framework
from ..models import Article


class ArticleFilter(FilterSet):
"""
ArticleFilter is a custom filter class which will filter based on the
article modeland the fields it will filter by are the article title,
author username and tags.ArticleFilter also overides the filter fields
lookup expressions from exact to icontains.
"""
title = rest_framework.CharFilter('title',
lookup_expr='icontains')
author = rest_framework.CharFilter('author__username',
lookup_expr='icontains')
tag = rest_framework.CharFilter('tag_list',
lookup_expr='icontains')

class Meta:
model = Article
fields = ("title", "author", "tag", )
8 changes: 6 additions & 2 deletions authors/apps/articles/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from rest_framework import status
from rest_framework.response import Response
from rest_framework import generics
from rest_framework import permissions
from rest_framework import generics, permissions, filters
from django_filters.rest_framework import DjangoFilterBackend

from . import (
serializers,
Expand All @@ -14,6 +14,7 @@

from .models import Rating, Article
from .paginators import ArticleLimitOffsetPagination
from .utils.custom_filters import ArticleFilter


class ArticlesApiView (generics.ListCreateAPIView):
Expand All @@ -25,6 +26,9 @@ class ArticlesApiView (generics.ListCreateAPIView):
serializer_class = serializers.ArticleSerializer
renderer_classes = (ArticleJSONRenderer,)
pagination_class = ArticleLimitOffsetPagination
filter_backends = (DjangoFilterBackend, filters.SearchFilter, )
filter_class = ArticleFilter
search_fields = ('author__username', 'description', 'body', 'title', )

def post(self, request):
data = request.data
Expand Down
18 changes: 14 additions & 4 deletions authors/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
'rest_framework',
'drf_yasg',
'mailer',
'django_filters',

'authors.apps.authentication',
'authors.apps.core',
Expand Down Expand Up @@ -84,18 +85,27 @@
# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators

USER_ATTRIBUTE_SIMILARITY_VALIDATOR = \
'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'
MINIMUM_LENGTH_VALIDATOR = \
'django.contrib.auth.password_validation.MinimumLengthValidator'
COMMON_PASSWORD_VALIDATOR = \
'django.contrib.auth.password_validation.CommonPasswordValidator'
NUMERIC_PASSWORD_VALIDATOR = \
'django.contrib.auth.password_validation.NumericPasswordValidator'

AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
'NAME': USER_ATTRIBUTE_SIMILARITY_VALIDATOR,
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'NAME': MINIMUM_LENGTH_VALIDATOR,
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
'NAME': COMMON_PASSWORD_VALIDATOR,
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
'NAME': NUMERIC_PASSWORD_VALIDATOR,
},
]

Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Django==2.1.5
django-cloudinary-storage==0.2.3
django-cors-middleware==1.3.1
django-extensions==2.1.4
django-filter==2.1.0
djangorestframework==3.9.1
docopt==0.6.2
drf-yasg==1.13.0
Expand Down

0 comments on commit b8dff4c

Please sign in to comment.