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 13, 2019
1 parent e953fed commit f0bfc5e
Show file tree
Hide file tree
Showing 5 changed files with 192 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), 1)
response = self.client.get(f"{self.url}?author=vdhfvsf")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 0)

def test_user_can_filter_by_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), 1)
response = self.client.get(f"{self.url}?title=vdhfvsf")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 0)

def test_user_can_filter_by_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), 1)
response = self.client.get(f"{self.url}?tag=vdhfvsf")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 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), 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), 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), 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), 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), 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), 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), 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), 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), 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), 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), 1)
response = self.client.get(f"{self.url}?search={title}")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)
response = self.client.get(f"{self.url}?search={description}")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)
response = self.client.get(f"{self.url}?search={body}")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)
response = self.client.get(f"{self.url}?search=vdhfvsf")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 0)
19 changes: 19 additions & 0 deletions authors/apps/articles/utils/custom_filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django_filters import FilterSet, rest_framework
from ..models import Article


class ArticleFilter(FilterSet):
"""
ArtcleFilter is a custom filter class which makes all my filter fields
case insensitive
"""
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 @@ -13,6 +13,7 @@
from ..profiles import models as profile_model

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


class ArticlesApiView (generics.ListCreateAPIView):
Expand All @@ -23,6 +24,9 @@ class ArticlesApiView (generics.ListCreateAPIView):
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
serializer_class = serializers.ArticleSerializer
renderer_classes = (ArticleJSONRenderer,)
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 f0bfc5e

Please sign in to comment.