[gen] Added param Field.mustIndex, that, for indexed fields, defines (via a method) when indexation must occur or not. Indeed, in some cases, an index is required on some field but we don't want all objects to have an index value for this field at any time.

This commit is contained in:
Gaetan Delannay 2014-12-26 17:29:14 +01:00
parent 4461cbb9a8
commit 8591611aac
16 changed files with 134 additions and 110 deletions

View file

@ -138,10 +138,11 @@ class Field:
</x>''')
def __init__(self, validator, multiplicity, default, show, page, group,
layouts, move, indexed, searchable, specificReadPermission,
specificWritePermission, width, height, maxChars, colspan,
master, masterValue, focus, historized, mapping, label,
sdefault, scolspan, swidth, sheight, persist, view, xml):
layouts, move, indexed, mustIndex, searchable,
specificReadPermission, specificWritePermission, width, height,
maxChars, colspan, master, masterValue, focus, historized,
mapping, label, sdefault, scolspan, swidth, sheight, persist,
view, xml):
# The validator restricts which values may be defined. It can be an
# interval (1,None), a list of string values ['choice1', 'choice2'],
# a regular expression, a custom function, a Selection instance, etc.
@ -167,6 +168,12 @@ class Field:
# If indexed is True, a database index will be set on the field for
# fast access.
self.indexed = indexed
# If "mustIndex", True by default, is specified, it must be a method
# returning a boolean value. Indexation will only occur when this value
# is True.
self.mustIndex = mustIndex
if not mustIndex and not callable(mustIndex):
raise Exception('Value for param "mustIndex" must be a method.')
# If specified "searchable", the field will be added to some global
# index allowing to perform application-wide, keyword searches.
self.searchable = searchable
@ -605,6 +612,9 @@ class Field:
If p_forSearch is True, it will return a "string" version of the
index value suitable for a global search.'''
# Must we produce an index value?
if not self.getAttribute(obj, 'mustIndex'): return
# Start by getting the field value on p_obj
res = self.getValue(obj)
# Zope catalog does not like unicode strings
if isinstance(res, unicode): res = res.encode('utf-8')

View file

@ -54,12 +54,12 @@ class Action(Field):
def __init__(self, validator=None, multiplicity=(1,1), default=None,
show=('view', 'result'), page='main', group=None, layouts=None,
move=0, indexed=False, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, maxChars=None, colspan=1, action=None,
result='computation', confirm=False, master=None,
masterValue=None, focus=False, historized=False, mapping=None,
label=None, icon=None, view=None, xml=None):
move=0, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, action=None, result='computation',
confirm=False, master=None, masterValue=None, focus=False,
historized=False, mapping=None, label=None, icon=None,
view=None, xml=None):
# Can be a single method or a list/tuple of methods
self.action = action
# For the 'result' param:
@ -77,7 +77,7 @@ class Action(Field):
# If no p_icon is specified, "action.png" will be used
self.icon = icon or 'action'
Field.__init__(self, None, (0,1), default, show, page, group, layouts,
move, indexed, False, specificReadPermission,
move, False, True, False, specificReadPermission,
specificWritePermission, width, height, None, colspan,
master, masterValue, focus, historized, mapping, label,
None, None, None, None, False, view, xml)

View file

@ -79,17 +79,18 @@ class Boolean(Field):
def __init__(self, validator=None, multiplicity=(0,1), default=None,
show=True, page='main', group=None, layouts = None, move=0,
indexed=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
sdefault=False, scolspan=1, swidth=None, sheight=None,
persist=True, render='checkbox', view=None, xml=None):
indexed=False, mustIndex=True, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, maxChars=None, colspan=1, master=None,
masterValue=None, focus=False, historized=False, mapping=None,
label=None, sdefault=False, scolspan=1, swidth=None,
sheight=None, persist=True, render='checkbox', view=None,
xml=None):
# By default, a boolean is edited via a checkbox. It can also be edited
# via 2 radio buttons (p_render="radios").
self.render = render
Field.__init__(self, validator, multiplicity, default, show, page,
group, layouts, move, indexed, searchable,
group, layouts, move, indexed, mustIndex, searchable,
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, mapping, label, sdefault, scolspan, swidth,

View file

@ -219,7 +219,7 @@ class Calendar(Field):
endDate=None, defaultDate=None, preCompute=None,
applicableEvents=None, view=None, xml=None):
Field.__init__(self, validator, (0,1), default, show, page, group,
layouts, move, False, False, specificReadPermission,
layouts, move, False, True, False, specificReadPermission,
specificWritePermission, width, height, None, colspan,
master, masterValue, focus, False, mapping, label, None,
None, None, None, True, view, xml)

View file

@ -32,13 +32,13 @@ class Computed(Field):
def __init__(self, validator=None, multiplicity=(0,1), default=None,
show=None, page='main', group=None, layouts=None, move=0,
indexed=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, method=None, formatMethod=None,
plainText=False, master=None, masterValue=None, focus=False,
historized=False, mapping=None, label=None, sdefault='',
scolspan=1, swidth=None, sheight=None, context=None, view=None,
xml=None):
indexed=False, mustIndex=True, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, maxChars=None, colspan=1, method=None,
formatMethod=None, plainText=False, master=None,
masterValue=None, focus=False, historized=False, mapping=None,
label=None, sdefault='', scolspan=1, swidth=None, sheight=None,
context=None, view=None, xml=None):
# The Python method used for computing the field value, or a PX.
self.method = method
# A specific method for producing the formatted value of this field.
@ -65,7 +65,7 @@ class Computed(Field):
# If method is a PX, its context can be given in p_context.
self.context = context
Field.__init__(self, None, multiplicity, default, show, page, group,
layouts, move, indexed, searchable,
layouts, move, indexed, mustIndex, searchable,
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, mapping, label, sdefault, scolspan, swidth,

View file

@ -165,12 +165,12 @@ class Date(Field):
startYear=time.localtime()[0]-10,
endYear=time.localtime()[0]+10, reverseYears=False,
show=True, page='main', group=None, layouts=None, move=0,
indexed=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
sdefault=None, scolspan=1, swidth=None, sheight=None,
persist=True, view=None, xml=None):
indexed=False, mustIndex=True, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, maxChars=None, colspan=1, master=None,
masterValue=None, focus=False, historized=False, mapping=None,
label=None, sdefault=None, scolspan=1, swidth=None,
sheight=None, persist=True, view=None, xml=None):
self.format = format
self.calendar = calendar
self.startYear = startYear
@ -179,7 +179,7 @@ class Date(Field):
# self.startYear to self.endYear will be listed in reverse order.
self.reverseYears = reverseYears
Field.__init__(self, validator, multiplicity, default, show, page,
group, layouts, move, indexed, searchable,
group, layouts, move, indexed, mustIndex, searchable,
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, mapping, label, sdefault, scolspan, swidth,

View file

@ -344,15 +344,14 @@ class File(Field):
def __init__(self, validator=None, multiplicity=(0,1), default=None,
show=True, page='main', group=None, layouts=None, move=0,
indexed=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
isImage=False, sdefault='', scolspan=1, swidth=None,
sheight=None, view=None, xml=None):
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, maxChars=None, colspan=1, master=None,
masterValue=None, focus=False, historized=False, mapping=None,
label=None, isImage=False, sdefault='', scolspan=1,
swidth=None, sheight=None, view=None, xml=None):
self.isImage = isImage
Field.__init__(self, validator, multiplicity, default, show, page,
group, layouts, move, indexed, False,
group, layouts, move, False, True, False,
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, mapping, label, sdefault, scolspan, swidth,

View file

@ -51,13 +51,13 @@ class Float(Field):
def __init__(self, validator=None, multiplicity=(0,1), default=None,
show=True, page='main', group=None, layouts=None, move=0,
indexed=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=5, height=None,
maxChars=13, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
sdefault=('',''), scolspan=1, swidth=None, sheight=None,
persist=True, precision=None, sep=(',', '.'), tsep=' ',
view=None, xml=None):
indexed=False, mustIndex=True, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=5, height=None, maxChars=13, colspan=1, master=None,
masterValue=None, focus=False, historized=False, mapping=None,
label=None, sdefault=('',''), scolspan=1, swidth=None,
sheight=None, persist=True, precision=None, sep=(',', '.'),
tsep=' ', view=None, xml=None):
# The precision is the number of decimal digits. This number is used
# for rendering the float, but the internal float representation is not
# rounded.
@ -75,7 +75,7 @@ class Float(Field):
'separator.' % sep)
self.tsep = tsep
Field.__init__(self, validator, multiplicity, default, show, page,
group, layouts, move, indexed, False,
group, layouts, move, indexed, mustIndex, searchable,
specificReadPermission, specificWritePermission, width,
height, maxChars, colspan, master, masterValue, focus,
historized, mapping, label, sdefault, scolspan, swidth,

View file

@ -26,13 +26,12 @@ class Info(Field):
def __init__(self, validator=None, multiplicity=(1,1), default=None,
show='view', page='main', group=None, layouts=None, move=0,
indexed=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
view=None, xml=None):
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, maxChars=None, colspan=1, master=None,
masterValue=None, focus=False, historized=False, mapping=None,
label=None, view=None, xml=None):
Field.__init__(self, None, (0,1), default, show, page, group, layouts,
move, indexed, False, specificReadPermission,
move, False, True, False, specificReadPermission,
specificWritePermission, width, height, None, colspan,
master, masterValue, focus, historized, mapping, label,
None, None, None, None, False, view, xml)

View file

@ -48,14 +48,14 @@ class Integer(Field):
def __init__(self, validator=None, multiplicity=(0,1), default=None,
show=True, page='main', group=None, layouts=None, move=0,
indexed=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=5, height=None,
maxChars=13, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
sdefault=('',''), scolspan=1, swidth=None, sheight=None,
persist=True, view=None, xml=None):
indexed=False, mustIndex=True, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=5, height=None, maxChars=13, colspan=1, master=None,
masterValue=None, focus=False, historized=False, mapping=None,
label=None, sdefault=('',''), scolspan=1, swidth=None,
sheight=None, persist=True, view=None, xml=None):
Field.__init__(self, validator, multiplicity, default, show, page,
group, layouts, move, indexed, searchable,
group, layouts, move, indexed, mustIndex, searchable,
specificReadPermission, specificWritePermission, width,
height, maxChars, colspan, master, masterValue, focus,
historized, mapping, label, sdefault, scolspan, swidth,

View file

@ -74,14 +74,13 @@ class List(Field):
def __init__(self, fields, validator=None, multiplicity=(0,1), default=None,
show=True, page='main', group=None, layouts=None, move=0,
indexed=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width='', height=None,
maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
subLayouts=Table('frv', width=None), widths=None, view=None,
xml=None):
specificReadPermission=False, specificWritePermission=False,
width='', height=None, maxChars=None, colspan=1, master=None,
masterValue=None, focus=False, historized=False, mapping=None,
label=None, subLayouts=Table('frv', width=None), widths=None,
view=None, xml=None):
Field.__init__(self, validator, multiplicity, default, show, page,
group, layouts, move, indexed, False,
group, layouts, move, False, True, False,
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, mapping, label, None, None, None, None,

View file

@ -51,7 +51,7 @@ class Ogone(Field):
colspan=1, master=None, masterValue=None, focus=False,
mapping=None, label=None, view=None, xml=None):
Field.__init__(self, None, (0,1), None, show, page, group, layouts,
move, False, False,specificReadPermission,
move, False, True, False,specificReadPermission,
specificWritePermission, width, height, None, colspan,
master, masterValue, focus, False, mapping, label, None,
None, None, None, False, view, xml)

View file

@ -150,16 +150,15 @@ class Pod(Field):
pxEdit = pxSearch = ''
def __init__(self, validator=None, default=None, show=('view', 'result'),
page='main', group=None, layouts=None, move=0, indexed=False,
searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
template=None, templateName=None, showTemplate=None,
freezeTemplate=None, maxPerRow=5, context=None,
stylesMapping={}, formats=None, getChecked=None, mailing=None,
mailingName=None, showMailing=None, mailingInfo=None,
view=None, xml=None):
page='main', group=None, layouts=None, move=0,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, maxChars=None, colspan=1, master=None,
masterValue=None, focus=False, historized=False, mapping=None,
label=None, template=None, templateName=None,
showTemplate=None, freezeTemplate=None, maxPerRow=5,
context=None, stylesMapping={}, formats=None, getChecked=None,
mailing=None, mailingName=None, showMailing=None,
mailingInfo=None, view=None, xml=None):
# Param "template" stores the path to the pod template(s). If there is
# a single template, a string is expected. Else, a list or tuple of
# strings is expected. Every such path must be relative to your
@ -273,7 +272,7 @@ class Pod(Field):
# self.mailing) and that returns an instance of class Mailing (above).
self.mailingInfo = mailingInfo
Field.__init__(self, None, (0,1), default, show, page, group, layouts,
move, indexed, searchable, specificReadPermission,
move, False, True, False, specificReadPermission,
specificWritePermission, width, height, None, colspan,
master, masterValue, focus, historized, mapping, label,
None, None, None, None, True, view, xml)

View file

@ -519,11 +519,11 @@ class Ref(Field):
afterLink=None, afterUnlink=None, back=None, show=True,
page='main', group=None, layouts=None, showHeaders=False,
shownInfo=None, select=None, maxPerPage=30, move=0,
indexed=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=5,
maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
queryable=False, queryFields=None, queryNbCols=1,
indexed=False, mustIndex=True, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=5, maxChars=None, colspan=1, master=None,
masterValue=None, focus=False, historized=False, mapping=None,
label=None, queryable=False, queryFields=None, queryNbCols=1,
navigable=False, changeOrder=True, numbered=False,
checkboxes=True, checkboxesDefault=None, sdefault='',
scolspan=1, swidth=None, sheight=None, sselect=None,
@ -710,7 +710,7 @@ class Ref(Field):
# alternative URL for the tied object that is shown within the menu.
self.menuUrlMethod = menuUrlMethod
Field.__init__(self, validator, multiplicity, default, show, page,
group, layouts, move, indexed, False,
group, layouts, move, indexed, mustIndex, searchable,
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, mapping, label, sdefault, scolspan, swidth,
@ -958,22 +958,29 @@ class Ref(Field):
def getIndexType(self): return 'ListIndex'
def getValidCatalogValue(self, value):
'''p_value is the new value we want to index in the catalog, for this
field, for some object. p_value as is may not be an acceptable value
for the catalog: if it it an empty list, instead of using it, the
catalog will keep the previously catalogued value! For this case,
this method produces an "empty" value that will really overwrite the
previous one. Moreover, the catalog does not like persistent
lists.'''
# The index does not like persistent lists. Moreover, I don't want to
# give to anyone access to the persistent list in the DB.
if value: return list(value)
# Ugly catalog: if I return an empty list, the previous value is kept
return ['']
def getIndexValue(self, obj, forSearch=False):
'''Value for indexing is the list of UIDs of linked objects. If
p_forSearch is True, it will return a list of the linked objects'
titles instead.'''
# Must we produce an index value?
if not self.getAttribute(obj, 'mustIndex'): return
if not forSearch:
res = getattr(obj.aq_base, self.name, None)
if res:
# The index does not like persistent lists. Moreover, I don't
# want to give to anyone access to the persistent list in the
# DB.
res = list(res)
else:
# Ugly catalog: if I return an empty list, the previous value
# is kept.
res = ['']
return res
return self.getValidCatalogValue(res)
else:
# For the global search: return linked objects' titles
return ' '.join([o.getShownValue('title') \

View file

@ -373,7 +373,7 @@ class String(Field):
def __init__(self, validator=None, multiplicity=(0,1), default=None,
format=LINE, show=True, page='main', group=None, layouts=None,
move=0, indexed=False, searchable=False,
move=0, indexed=False, mustIndex=True, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, maxChars=None, colspan=1, master=None,
masterValue=None, focus=False, historized=False, mapping=None,
@ -421,7 +421,7 @@ class String(Field):
# that can, for example, return an internationalized value.
self.placeholder = placeholder
Field.__init__(self, validator, multiplicity, default, show, page,
group, layouts, move, indexed, searchable,
group, layouts, move, indexed, mustIndex, searchable,
specificReadPermission, specificWritePermission, width,
height, maxChars, colspan, master, masterValue, focus,
historized, mapping, label, sdefault, scolspan, swidth,
@ -684,12 +684,29 @@ class String(Field):
'''Extracts pure text from XHTML p_value.'''
return XhtmlTextExtractor(raiseOnError=False).parse('<p>%s</p>' % value)
emptyStringTuple = ('',)
emptyValuesCatalogIgnored = (None, '')
def getValidCatalogValue(self, value, forSearch):
'''p_value is the new value we want to index in the catalog, for this
field, for some object. p_value as is may not be an acceptable value
for the catalog: if it represents some empty value, like an empty
string, None or an empty tuple, instead of using it, the catalog will
keep the previously catalogued value! For those cases, this method
produces "empty" values that will really overwrite previous ones.'''
# Ugly catalog: if I give an empty tuple as index value, it keeps the
# previous value. If I give him a tuple containing an empty string, it
# is ok.
if isinstance(value, tuple) and not value:
value = forSearch and ' ' or ('',)
# Ugly catalog: if value is an empty string or None, it keeps the
# previous index value.
elif value in (None, ''): return ' '
return value
def getIndexValue(self, obj, forSearch=False):
'''Pure text must be extracted from rich content; multilingual content
must be concatenated.'''
# Must we produce an index value?
if not self.getAttribute(obj, 'mustIndex'):
return self.getValidCatalogValue(None, forSearch)
isXhtml = self.format == String.XHTML
if self.isMultilingual(obj):
res = self.getValue(obj)
@ -703,14 +720,7 @@ class String(Field):
else:
res = Field.getIndexValue(self, obj, forSearch)
if res and isXhtml: res = self.extractText(res)
# Ugly catalog: if I give an empty tuple as index value, it keeps the
# previous value. If I give him a tuple containing an empty string, it
# is ok.
if isinstance(res, tuple) and not res: res = self.emptyStringTuple
# Ugly catalog: if value is an empty string or None, it keeps the
# previous index value.
if res in self.emptyValuesCatalogIgnored: res = ' '
return res
return self.getValidCatalogValue(res, forSearch)
def getPossibleValues(self, obj, withTranslations=False,
withBlankValue=False, className=None,

View file

@ -217,7 +217,7 @@ def executeCommand(cmd):
return res
# ------------------------------------------------------------------------------
charsIgnore = u'.,:;*+=~?%^\'"<>{}[]|\t\\'
charsIgnore = u'.,:;*+=~?%^\'"<>{}[]|\t\\°'
fileNameIgnore = charsIgnore + u' $£€/'
extractIgnore = charsIgnore + '()'
alphaRex = re.compile('[a-zA-Z]')