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:
@@ -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
|
||||
|
||||
@@ -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'
|
||||
)
|
||||
|
||||
@@ -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.')
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}
|
||||
)
|
||||
|
||||
|
||||
@@ -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()
|
||||
),
|
||||
]
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user