Update dynamic search app

Sort methods. Update use of .filter_by_access() to
.restrict_queryset().

Change the method to so the final object
filtering. Instead of expressing the pk list and remove the
duplicated using a set, pass the queryset as a subquery to
the object filter. This moves the processing to the database
instead of holding a list of an unknown number of primary
keys in the memory.

Add keyword arguments.

Update tests to use the latest user test case mixin interface.

Signed-off-by: Roberto Rosario <Roberto.Rosario@mayan-edms.com>
This commit is contained in:
Roberto Rosario
2019-01-21 03:53:55 -04:00
parent 166183dff9
commit ad7c77b4f3
8 changed files with 95 additions and 101 deletions

View File

@@ -107,6 +107,38 @@ class SearchModel(object):
def __str__(self):
return force_text(self.label)
def add_model_field(self, *args, **kwargs):
"""
Add a search field that directly belongs to the parent SearchModel
"""
search_field = SearchField(self, *args, **kwargs)
self.search_fields.append(search_field)
def get_fields_simple_list(self):
"""
Returns a list of the fields for the SearchModel
"""
result = []
for search_field in self.search_fields:
result.append((search_field.get_full_name(), search_field.label))
return result
def get_full_name(self):
return '%s.%s' % (self.app_label, self.model_name)
def get_search_field(self, full_name):
try:
return self.search_fields[full_name]
except KeyError:
raise KeyError('No search field named: %s' % full_name)
def get_search_query(self, query_string, global_and_search=False):
return SearchQuery(
query_string=query_string, search_model=self,
global_and_search=global_and_search
)
@property
def label(self):
if not self._label:
@@ -123,38 +155,6 @@ class SearchModel(object):
def pk(self):
return self.get_full_name()
def add_model_field(self, *args, **kwargs):
"""
Add a search field that directly belongs to the parent SearchModel
"""
search_field = SearchField(self, *args, **kwargs)
self.search_fields.append(search_field)
def get_full_name(self):
return '%s.%s' % (self.app_label, self.model_name)
def get_fields_simple_list(self):
"""
Returns a list of the fields for the SearchModel
"""
result = []
for search_field in self.search_fields:
result.append((search_field.get_full_name(), search_field.label))
return result
def get_search_field(self, full_name):
try:
return self.search_fields[full_name]
except KeyError:
raise KeyError('No search field named: %s' % full_name)
def get_search_query(self, query_string, global_and_search=False):
return SearchQuery(
query_string=query_string, search_model=self,
global_and_search=global_and_search
)
def search(self, query_string, user, global_and_search=False):
AccessControlList = apps.get_model(
app_label='acls', model_name='AccessControlList'
@@ -165,18 +165,14 @@ class SearchModel(object):
)
queryset = self.model.objects.filter(
pk__in=set(
self.model.objects.filter(search_query.query).values_list(
'pk', flat=True
)[
:setting_limit.value
]
)
pk__in=self.model.objects.filter(search_query.query).values('pk')[
:setting_limit.value
]
)
if self.permission:
queryset = AccessControlList.objects.filter_by_access(
permission=self.permission, user=user, queryset=queryset
queryset = AccessControlList.objects.restrict_queryset(
permission=self.permission, queryset=queryset, user=user
)
return queryset

View File

@@ -5,13 +5,14 @@ from django.utils.translation import ugettext_lazy as _
from mayan.apps.navigation import Link
link_search = Link(
text=_('Search'), view='search:search', args='search_model.get_full_name'
kwargs={'search_model': 'search_model.get_full_name'}, text=_('Search'),
view='search:search'
)
link_search_advanced = Link(
text=_('Advanced search'), view='search:search_advanced',
args='search_model.get_full_name'
kwargs={'search_model': 'search_model.get_full_name'},
text=_('Advanced search'), view='search:search_advanced'
)
link_search_again = Link(
text=_('Search again'), view='search:search_again',
args='search_model.get_full_name', keep_query=True
kwargs={'search_model': 'search_model.get_full_name'}, keep_query=True,
text=_('Search again'), view='search:search_again'
)

View File

@@ -4,7 +4,8 @@ from django.utils.translation import ugettext_lazy as _
from mayan.apps.smart_settings import Namespace
namespace = Namespace(name='dynamic_search', label=_('Search'))
namespace = Namespace(label=_('Search'), name='dynamic_search')
setting_limit = namespace.add_setting(
global_name='SEARCH_LIMIT', default=100,
help_text=_('Maximum amount search hits to fetch and display.')

View File

@@ -23,9 +23,9 @@ class SearchAPITestCase(DocumentTestMixin, BaseAPITestCase):
return self.get(
path='{}?q={}'.format(
reverse(
'rest_api:search-view', args=(
document_search.get_full_name(),
)
viewname='rest_api:search-view', kwargs={
'search_model': document_search.get_full_name()
}
), self.document.label
)
)

View File

@@ -9,6 +9,8 @@ from mayan.apps.documents.tests import (
class DocumentSearchTestCase(DocumentTestMixin, BaseTestCase):
auto_upload_document = False
create_test_case_superuser = True
create_test_case_user = False
test_document_filename = TEST_DOCUMENT_FILENAME
def test_simple_search_after_related_name_change(self):
@@ -18,7 +20,7 @@ class DocumentSearchTestCase(DocumentTestMixin, BaseTestCase):
"""
self.document = self.upload_document()
queryset = document_search.search(
{'q': 'Mayan'}, user=self.admin_user
{'q': 'Mayan'}, user=self._test_case_superuser
)
self.assertEqual(queryset.count(), 1)
self.assertTrue(self.document in queryset)
@@ -27,7 +29,7 @@ class DocumentSearchTestCase(DocumentTestMixin, BaseTestCase):
# Test versions__filename
self.document = self.upload_document()
queryset = document_search.search(
{'label': self.document.label}, user=self.admin_user
{'label': self.document.label}, user=self._test_case_superuser
)
self.assertEqual(queryset.count(), 1)
self.assertTrue(self.document in queryset)
@@ -35,7 +37,7 @@ class DocumentSearchTestCase(DocumentTestMixin, BaseTestCase):
# Test versions__mimetype
queryset = document_search.search(
{'versions__mimetype': self.document.file_mimetype},
user=self.admin_user
user=self._test_case_superuser
)
self.assertEqual(queryset.count(), 1)
self.assertTrue(self.document in queryset)
@@ -48,7 +50,7 @@ class DocumentSearchTestCase(DocumentTestMixin, BaseTestCase):
self.document_2.save()
queryset = document_search.search(
{'q': 'Mayan OR second'}, user=self.admin_user
{'q': 'Mayan OR second'}, user=self._test_case_superuser
)
self.assertEqual(queryset.count(), 2)
self.assertTrue(self.document in queryset)
@@ -61,12 +63,12 @@ class DocumentSearchTestCase(DocumentTestMixin, BaseTestCase):
self.document_2.save()
queryset = document_search.search(
{'q': 'non_valid second'}, user=self.admin_user
{'q': 'non_valid second'}, user=self._test_case_superuser
)
self.assertEqual(queryset.count(), 0)
queryset = document_search.search(
{'q': 'second non_valid'}, user=self.admin_user
{'q': 'second non_valid'}, user=self._test_case_superuser
)
self.assertEqual(queryset.count(), 0)
@@ -77,26 +79,26 @@ class DocumentSearchTestCase(DocumentTestMixin, BaseTestCase):
self.document_2.save()
queryset = document_search.search(
{'q': '-non_valid second'}, user=self.admin_user
{'q': '-non_valid second'}, user=self._test_case_superuser
)
self.assertEqual(queryset.count(), 1)
queryset = document_search.search(
{'label': '-second'}, user=self.admin_user
{'label': '-second'}, user=self._test_case_superuser
)
self.assertEqual(queryset.count(), 0)
queryset = document_search.search(
{'label': '-second -Mayan'}, user=self.admin_user
{'label': '-second -Mayan'}, user=self._test_case_superuser
)
self.assertEqual(queryset.count(), 0)
queryset = document_search.search(
{'label': '-second OR -Mayan'}, user=self.admin_user
{'label': '-second OR -Mayan'}, user=self._test_case_superuser
)
self.assertEqual(queryset.count(), 1)
queryset = document_search.search(
{'label': '-non_valid -second'}, user=self.admin_user
{'label': '-non_valid -second'}, user=self._test_case_superuser
)
self.assertEqual(queryset.count(), 0)

View File

@@ -1,44 +1,32 @@
from __future__ import unicode_literals
from mayan.apps.common.tests import GenericViewTestCase
from mayan.apps.documents.models import DocumentType
from mayan.apps.documents.search import document_search
from mayan.apps.documents.tests import (
TEST_DOCUMENT_TYPE_LABEL, TEST_SMALL_DOCUMENT_PATH
)
from mayan.apps.documents.tests import DocumentTestMixin
class Issue46TestCase(GenericViewTestCase):
class Issue46TestCase(DocumentTestMixin, GenericViewTestCase):
"""
Functional tests to make sure issue 46 is fixed
"""
auto_upload_document = False
auto_login_superuser = True
create_test_case_superuser = True
create_test_case_user = False
def setUp(self):
super(Issue46TestCase, self).setUp()
self.login_admin_user()
self.document_count = 4
self.document_type = DocumentType.objects.create(
label=TEST_DOCUMENT_TYPE_LABEL
)
# Upload many instances of the same test document
for i in range(self.document_count):
with open(TEST_SMALL_DOCUMENT_PATH, mode='rb') as file_object:
self.document_type.new_document(
file_object=file_object,
label='test document',
)
def tearDown(self):
for document_type in DocumentType.objects.all():
document_type.delete()
super(Issue46TestCase, self).tearDown()
self.test_document = self.upload_document()
def test_advanced_search_past_first_page(self):
# Make sure all documents are returned by the search
queryset = document_search.search(
{'label': 'test document'}, user=self.admin_user
{'label': self.test_document.label}, user=self._test_case_superuser
)
self.assertEqual(queryset.count(), self.document_count)
@@ -46,7 +34,7 @@ class Issue46TestCase(GenericViewTestCase):
# Functional test for the first page of advanced results
response = self.get(
viewname='search:results',
args=(document_search.get_full_name(),),
kwargs={'search_model': document_search.get_full_name()},
data={'label': 'test'}
)
@@ -66,7 +54,7 @@ class Issue46TestCase(GenericViewTestCase):
# Functional test for the second page of advanced results
response = self.get(
viewname='search:results',
args=(document_search.get_full_name(),),
kwargs={'search_model': document_search.get_full_name()},
data={'label': 'test', 'page': 2}
)

View File

@@ -6,32 +6,35 @@ from .api_views import APIAdvancedSearchView, APISearchModelList, APISearchView
from .views import AdvancedSearchView, ResultsView, SearchAgainView, SearchView
urlpatterns = [
url(r'^(?P<search_model>[\.\w]+)/$', SearchView.as_view(), name='search'),
url(
r'^advanced/(?P<search_model>[\.\w]+)/$', AdvancedSearchView.as_view(),
name='search_advanced'
regex=r'^(?P<search_model>[\.\w]+)/$', name='search',
view=SearchView.as_view()
),
url(
r'^again/(?P<search_model>[\.\w]+)/$', SearchAgainView.as_view(),
name='search_again'
regex=r'^advanced/(?P<search_model>[\.\w]+)/$', name='search_advanced',
view=AdvancedSearchView.as_view()
),
url(
r'^results/(?P<search_model>[\.\w]+)/$', ResultsView.as_view(),
name='results'
regex=r'^again/(?P<search_model>[\.\w]+)/$', name='search_again',
view=SearchAgainView.as_view()
),
url(
regex=r'^results/(?P<search_model>[\.\w]+)/$', name='results',
view=ResultsView.as_view()
)
]
api_urls = [
url(
r'^search_models/$', APISearchModelList.as_view(),
name='searchmodel-list'
regex=r'^search_models/$', name='searchmodel-list',
view=APISearchModelList.as_view()
),
url(
r'^search/(?P<search_model>[\.\w]+)/$', APISearchView.as_view(),
name='search-view'
regex=r'^search/(?P<search_model>[\.\w]+)/$', name='search-view',
view=APISearchView.as_view()
),
url(
r'^search/advanced/(?P<search_model>[\.\w]+)/$', APIAdvancedSearchView.as_view(),
name='advanced-search-view'
regex=r'^search/advanced/(?P<search_model>[\.\w]+)/$',
name='advanced-search-view', view=APIAdvancedSearchView.as_view()
),
]

View File

@@ -47,7 +47,8 @@ class ResultsView(SearchModelMixin, SingleObjectListView):
global_and_search = False
return self.search_model.get_search_query(
query_string=self.request.GET, global_and_search=global_and_search
global_and_search=global_and_search,
query_string=self.request.GET
)
def get_object_list(self):
@@ -63,8 +64,8 @@ class ResultsView(SearchModelMixin, SingleObjectListView):
global_and_search = False
queryset = self.search_model.search(
query_string=self.request.GET, user=self.request.user,
global_and_search=global_and_search
global_and_search=global_and_search,
query_string=self.request.GET, user=self.request.user
)
return queryset
@@ -79,7 +80,9 @@ class SearchView(SearchModelMixin, SimpleView):
return {
'form': self.get_form(),
'form_action': reverse(
'search:results', args=(self.search_model.get_full_name(),)
viewname='search:results', kwargs={
'search_model': self.search_model.get_full_name()
}
),
'search_model': self.search_model,
'submit_icon_class': icon_search_submit,