[gen] Class.listColumns can now be a static method (accepting the tool as single arg). [gen] Indexed Ref fields are now sortable. For every such field, 2 indexes are created: a list index for searching, and a standard index for sorting (by their title).
This commit is contained in:
parent
5c41f1b3d2
commit
c11002b7d5
8 changed files with 100 additions and 66 deletions
|
@ -325,6 +325,7 @@ class Field:
|
|||
'''Can fields of this type be used for sorting purposes (when sorting
|
||||
search results (p_usage="search") or when sorting reference fields
|
||||
(p_usage="ref")?'''
|
||||
if self.name == 'state': return
|
||||
if usage == 'search':
|
||||
return self.indexed and not self.isMultiValued() and not \
|
||||
((self.type == 'String') and self.isSelection())
|
||||
|
@ -576,7 +577,7 @@ class Field:
|
|||
If p_forSearch is True, it will return a "string" version of the
|
||||
index value suitable for a global search.'''
|
||||
res = self.getValue(obj)
|
||||
# Zope catalog does not like unicode strings.
|
||||
# Zope catalog does not like unicode strings
|
||||
if isinstance(res, unicode): res = res.encode('utf-8')
|
||||
if forSearch and (res != None):
|
||||
if type(res) in sutils.sequenceTypes:
|
||||
|
@ -589,6 +590,29 @@ class Field:
|
|||
res = str(res)
|
||||
return res
|
||||
|
||||
def getIndexName(self, usage='search'):
|
||||
'''Gets the name of the Zope index that corresponds to this field.
|
||||
Indexes can be used for searching (p_usage="search") or for sorting
|
||||
(usage="sort"). The method returns None if the field
|
||||
named p_fieldName can't be used for p_usage.'''
|
||||
# Manage special cases
|
||||
if self.name == 'title':
|
||||
# For field 'title', Appy has a specific index 'SortableTitle',
|
||||
# because index 'Title' is a TextIndex (for searchability) and can't
|
||||
# be used for sorting.
|
||||
return (usage == 'sort') and 'SortableTitle' or 'Title'
|
||||
elif self.name == 'state': return 'State'
|
||||
else:
|
||||
res = 'get%s%s'% (self.name[0].upper(), self.name[1:])
|
||||
if (usage == 'sort') and self.hasSortIndex(): res += '_sort'
|
||||
return res
|
||||
|
||||
def hasSortIndex(self):
|
||||
'''Some fields have indexes that prevents sorting (ie, list indexes).
|
||||
Those fields may define a secondary index, specifically for sorting.
|
||||
This is the case of Ref fields for example.'''
|
||||
return
|
||||
|
||||
def getCatalogValue(self, obj, usage='search'):
|
||||
'''This method returns the index value that is currently stored in the
|
||||
catalog for this field on p_obj.'''
|
||||
|
@ -596,7 +620,7 @@ class Field:
|
|||
raise Exception('Field %s: cannot retrieve catalog version of ' \
|
||||
'unindexed field.' % self.name)
|
||||
tool = obj.getTool()
|
||||
indexName = Search.getIndexName(self.name, usage=usage)
|
||||
indexName = self.getIndexName(usage)
|
||||
catalogBrain = tool.getObject(obj.id, brain=True)
|
||||
index = tool.getApp().catalog.Indexes[indexName]
|
||||
return index.getEntryForObject(catalogBrain.getRID())
|
||||
|
|
|
@ -182,7 +182,7 @@ class Ref(Field):
|
|||
# ref field according to the field that corresponds to this column.
|
||||
pxSortIcons = Px('''
|
||||
<x if="changeOrder and (len(objects) > 1) and \
|
||||
ztool.isSortable(refField.name, tiedClassName, 'ref')"
|
||||
refField.isSortable(usage='ref')"
|
||||
var2="ajaxBaseCall=navBaseCall.replace('**v**', '%s,%s,{%s:%s,%s:%s}'% \
|
||||
(q(startNumber), q('sort'), q('sortKey'), q(refField.name), \
|
||||
q('reverse'), q('**v**')))">
|
||||
|
@ -963,10 +963,14 @@ class Ref(Field):
|
|||
res = ['']
|
||||
return res
|
||||
else:
|
||||
# For the global search: return linked objects' titles.
|
||||
res = [o.title for o in self.getValue()]
|
||||
if not res: res.append('')
|
||||
return res
|
||||
# For the global search: return linked objects' titles
|
||||
return ' '.join([o.getShownValue('title') \
|
||||
for o in self.getValue(obj, appy=False)])
|
||||
|
||||
def hasSortIndex(self):
|
||||
'''An indexed Ref field is of type "ListIndex", which is not sortable.
|
||||
So an additional FieldIndex is required.'''
|
||||
return True
|
||||
|
||||
def validateValue(self, obj, value):
|
||||
if not self.link: return
|
||||
|
|
|
@ -55,23 +55,19 @@ class Search:
|
|||
self.checkboxesDefault = checkboxesDefault
|
||||
|
||||
@staticmethod
|
||||
def getIndexName(fieldName, usage='search'):
|
||||
'''Gets the name of the technical index that corresponds to field named
|
||||
p_fieldName. Indexes can be used for searching (p_usage="search") or
|
||||
for sorting (usage="sort"). The method returns None if the field
|
||||
named p_fieldName can't be used for p_usage.'''
|
||||
if fieldName == 'title':
|
||||
if usage == 'search': return 'Title'
|
||||
else: return 'SortableTitle'
|
||||
# Indeed, for field 'title', Appy has a specific index
|
||||
# 'SortableTitle', because index 'Title' is a TextIndex
|
||||
# (for searchability) and can't be used for sorting.
|
||||
elif fieldName == 'state': return 'State'
|
||||
elif fieldName == 'created': return 'Created'
|
||||
elif fieldName == 'modified': return 'Modified'
|
||||
elif fieldName in defaultIndexes: return fieldName
|
||||
def getIndexName(name, klass, usage='search'):
|
||||
'''Gets the name of the Zope index that corresponds to p_name. Indexes
|
||||
can be used for searching (p_usage="search") or for sorting
|
||||
(usage="sort"). The method returns None if the field named
|
||||
p_name can't be used for p_usage.'''
|
||||
# Manage indexes that do not have a corresponding field
|
||||
if name == 'created': return 'Created'
|
||||
elif name == 'modified': return 'Modified'
|
||||
elif name in defaultIndexes: return name
|
||||
else:
|
||||
return 'get%s%s'% (fieldName[0].upper(),fieldName[1:])
|
||||
# Manage indexes corresponding to fields
|
||||
field = getattr(klass, name, None)
|
||||
if field: return field.getIndexName(usage)
|
||||
|
||||
@staticmethod
|
||||
def getSearchValue(fieldName, fieldValue, klass):
|
||||
|
@ -116,30 +112,28 @@ class Search:
|
|||
sortBy and sortOrder (and not "resolve" them to Zope's sort_on and
|
||||
sort_order).'''
|
||||
# Put search criteria in p_criteria
|
||||
for fieldName, fieldValue in self.fields.iteritems():
|
||||
for name, value in self.fields.iteritems():
|
||||
# Management of searches restricted to objects linked through a
|
||||
# Ref field: not implemented yet.
|
||||
if fieldName == '_ref': continue
|
||||
if name == '_ref': continue
|
||||
# Make the correspondence between the name of the field and the
|
||||
# name of the corresponding index, excepted if advanced is True: in
|
||||
# that case, the correspondence will be done later.
|
||||
if not advanced:
|
||||
attrName = Search.getIndexName(fieldName)
|
||||
indexName = Search.getIndexName(name, klass)
|
||||
# Express the field value in the way needed by the index
|
||||
criteria[attrName] = Search.getSearchValue(fieldName,
|
||||
fieldValue, klass)
|
||||
criteria[indexName] = Search.getSearchValue(name, value, klass)
|
||||
else:
|
||||
criteria[fieldName]= fieldValue
|
||||
criteria[name] = value
|
||||
# Add a sort order if specified
|
||||
if self.sortBy:
|
||||
c = criteria
|
||||
if not advanced:
|
||||
criteria['sort_on'] = Search.getIndexName(self.sortBy,
|
||||
usage='sort')
|
||||
if self.sortOrder == 'desc': criteria['sort_order'] = 'reverse'
|
||||
else: criteria['sort_order'] = None
|
||||
c['sort_on']=Search.getIndexName(self.sortBy,klass,usage='sort')
|
||||
c['sort_order']= (self.sortOrder=='desc') and 'reverse' or None
|
||||
else:
|
||||
criteria['sortBy'] = self.sortBy
|
||||
criteria['sortOrder'] = self.sortOrder
|
||||
c['sortBy'] = self.sortBy
|
||||
c['sortOrder'] = self.sortOrder
|
||||
|
||||
def isShowable(self, klass, tool):
|
||||
'''Is this Search instance (defined in p_klass) showable?'''
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue