Task #436 - Filter user stories by subject
parent
6ec9489bcc
commit
6229770c0f
|
@ -13,7 +13,8 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
import operator
|
||||||
|
from functools import reduce
|
||||||
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.db.models.sql.where import ExtraWhere, OR
|
from django.db.models.sql.where import ExtraWhere, OR
|
||||||
|
@ -214,3 +215,28 @@ class TagsFilter(FilterBackend):
|
||||||
queryset = tags.filter(queryset, contains=query_tags)
|
queryset = tags.filter(queryset, contains=query_tags)
|
||||||
|
|
||||||
return super().filter_queryset(request, queryset, view)
|
return super().filter_queryset(request, queryset, view)
|
||||||
|
|
||||||
|
|
||||||
|
class SearchFieldFilter(filters.SearchFilter):
|
||||||
|
"""Search filter that looks up the search param in the parameter named after the search field,
|
||||||
|
that is: ?<search field>=... instead of looking for the search param: ?search=...
|
||||||
|
This way you can search in a field-specific way.
|
||||||
|
"""
|
||||||
|
def get_search_terms(self, request, field):
|
||||||
|
params = request.QUERY_PARAMS.get(field, '')
|
||||||
|
return params.replace(',', ' ').split()
|
||||||
|
|
||||||
|
def filter_queryset(self, request, queryset, view):
|
||||||
|
search_fields = getattr(view, "search_fields", None)
|
||||||
|
if not search_fields:
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
lookups = dict((self.construct_search(field), self.get_search_terms(request, field))
|
||||||
|
for field in search_fields)
|
||||||
|
|
||||||
|
for lookup, values in lookups.items():
|
||||||
|
or_queries = [Q(**{lookup: value}) for value in values]
|
||||||
|
if or_queries:
|
||||||
|
queryset = queryset.filter(reduce(operator.or_, or_queries))
|
||||||
|
|
||||||
|
return queryset
|
||||||
|
|
|
@ -48,9 +48,11 @@ class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMi
|
||||||
list_serializer_class = serializers.UserStorySerializer
|
list_serializer_class = serializers.UserStorySerializer
|
||||||
permission_classes = (permissions.UserStoryPermission,)
|
permission_classes = (permissions.UserStoryPermission,)
|
||||||
|
|
||||||
filter_backends = (filters.CanViewUsFilterBackend, filters.TagsFilter)
|
filter_backends = (filters.CanViewUsFilterBackend, filters.TagsFilter,
|
||||||
|
filters.SearchFieldFilter)
|
||||||
retrieve_exclude_filters = (filters.TagsFilter,)
|
retrieve_exclude_filters = (filters.TagsFilter,)
|
||||||
filter_fields = ['project', 'milestone', 'milestone__isnull', 'status', 'is_archived']
|
filter_fields = ['project', 'milestone', 'milestone__isnull', 'status', 'is_archived']
|
||||||
|
search_fields = ('subject',)
|
||||||
|
|
||||||
# Specific filter used for filtering neighbor user stories
|
# Specific filter used for filtering neighbor user stories
|
||||||
_neighbor_tags_filter = filters.TagsFilter('neighbor_tags')
|
_neighbor_tags_filter = filters.TagsFilter('neighbor_tags')
|
||||||
|
|
|
@ -51,3 +51,16 @@ def test_api_delete_userstory(client):
|
||||||
response = client.delete(url)
|
response = client.delete(url)
|
||||||
|
|
||||||
assert response.status_code == 204
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_filter_by_subject(client):
|
||||||
|
f.create_userstory()
|
||||||
|
us = f.create_userstory(subject="some random subject")
|
||||||
|
url = reverse("userstories-list") + "?subject=some subject"
|
||||||
|
|
||||||
|
client.login(us.owner)
|
||||||
|
response = client.get(url)
|
||||||
|
number_of_stories = len(response.data)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert number_of_stories == 1, number_of_stories
|
||||||
|
|
Loading…
Reference in New Issue