[gen] Added field.persist to avoid storing values for fields that do not require it (like master fields only used to determine selectable values among slave fields).

This commit is contained in:
Gaetan Delannay 2014-03-04 15:03:37 +01:00
parent b8ceb66a49
commit ea08d7981f
19 changed files with 106 additions and 75 deletions

View file

@ -61,8 +61,7 @@ class Field:
value=zobj.getFormattedFieldValue(name, rawValue, showChanges);
requestValue=zobj.getRequestFieldValue(name);
inRequest=req.has_key(name);
errors=errors|();
inError=name in errors;
error=req.get('%s_error' % name);
isMultiple=(field.multiplicity[1] == None) or \
(field.multiplicity[1] > 1);
masterCss=field.slaves and ('master_%s' % name) or '';
@ -86,8 +85,8 @@ class Field:
src=":url('help')"/></acronym>''')
# Displays validation-error-related info about a field.
pxValidation = Px('''<x><acronym if="inError" title=":errors[name]"><img
src=":url('warning')"/></acronym><img if="not inError"
pxValidation = Px('''<x><acronym if="error" title=":error"><img
src=":url('warning')"/></acronym><img if="not error"
src=":url('warning_no.gif')"/></x>''')
# Displays the fact that a field is required.
@ -107,7 +106,7 @@ class Field:
layouts, move, indexed, searchable, specificReadPermission,
specificWritePermission, width, height, maxChars, colspan,
master, masterValue, focus, historized, sync, mapping, label,
sdefault, scolspan, swidth, sheight):
sdefault, scolspan, swidth, sheight, persist):
# 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.
@ -215,6 +214,10 @@ class Field:
# Width and height for the search widget
self.swidth = swidth or width
self.sheight = sheight or height
# "persist" indicates if field content must be stored in the database.
# For some fields it is not wanted (ie, fields used only as masters to
# update slave's selectable values).
self.persist = persist
def init(self, name, klass, appName):
'''When the application server starts, this secondary constructor is
@ -584,26 +587,14 @@ class Field:
res += '+'
return res
def getOnChange(self, name, zobj, layoutType):
def getOnChange(self, zobj, layoutType):
'''When this field is a master, this method computes the call to the
Javascript function that will be called when its value changes (in
order to update slaves).'''
if not self.slaves: return ''
q = zobj.getTool().quote
# Create the dict of request values for slave fields.
rvs = {}
req = zobj.REQUEST
for slave in self.slaves:
name = slave.name
if not req.has_key(name): continue
if not req[name]: continue
rvs[name] = req[name]
if rvs:
rvs = ',%s' % sutils.getStringDict(rvs)
else:
rvs = ''
return 'updateSlaves(this,null,%s,%s%s)' % \
(q(zobj.absolute_url()), q(layoutType), rvs)
return 'updateSlaves(this,null,%s,%s)' % \
(q(zobj.absolute_url()), q(layoutType))
def isEmptyValue(self, value, obj=None):
'''Returns True if the p_value must be considered as an empty value.'''
@ -680,7 +671,7 @@ class Field:
def store(self, obj, value):
'''Stores the p_value (produced by m_getStorableValue) that complies to
p_self type definition on p_obj.'''
setattr(obj, self.name, value)
if self.persist: setattr(obj, self.name, value)
def callMethod(self, obj, method, cache=True):
'''This method is used to call a p_method on p_obj. p_method is part of

View file

@ -74,7 +74,7 @@ class Action(Field):
move, indexed, False, specificReadPermission,
specificWritePermission, width, height, None, colspan,
master, masterValue, focus, historized, False, mapping,
label, None, None, None, None)
label, None, None, None, None, False)
self.validable = False
def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'}

View file

@ -23,19 +23,17 @@ from appy.gen.layout import Table
class Boolean(Field):
'''Field for storing boolean values.'''
pxView = pxCell = Px('''
<x><x>:value</x>
<input type="hidden" if="masterCss"
class=":masterCss" value=":rawValue" name=":name" id=":name"/>
</x>''')
pxView = pxCell = Px('''<x>:value</x>
<input type="hidden" if="masterCss"
class=":masterCss" value=":rawValue" name=":name" id=":name"/>''')
pxEdit = Px('''
<x var="isChecked=field.isChecked(zobj, rawValue)">
<input type="checkbox" name=":name + '_visible'" id=":name"
class=":masterCss" checked=":isChecked"
onclick=":'toggleCheckbox(%s, %s); %s' % (q(name), \
q('%s_hidden' % name), field.getOnChange(name, zobj, \
layoutType))"/>
q('%s_hidden' % name), \
field.getOnChange(zobj, layoutType))"/>
<input type="hidden" name=":name" id=":'%s_hidden' % name"
value=":isChecked and 'True' or 'False'"/>
</x>''')
@ -64,13 +62,14 @@ class Boolean(Field):
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):
sdefault=False, scolspan=1, swidth=None, sheight=None,
persist=True):
Field.__init__(self, validator, multiplicity, default, show, page,
group, layouts, move, indexed, searchable,
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, True, mapping, label, sdefault, scolspan,
swidth, sheight)
swidth, sheight, persist)
self.pythonType = bool
# Layout including a description

View file

@ -222,7 +222,7 @@ class Calendar(Field):
layouts, move, False, False, specificReadPermission,
specificWritePermission, width, height, None, colspan,
master, masterValue, focus, False, True, mapping, label,
None, None, None, None)
None, None, None, None, True)
# eventTypes can be a "static" list or tuple of strings that identify
# the types of events that are supported by this calendar. It can also
# be a method that computes such a "dynamic" list or tuple. When

View file

@ -70,7 +70,7 @@ class Computed(Field):
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, sync, mapping, label, sdefault, scolspan,
swidth, sheight)
swidth, sheight, False)
self.validable = False
def getValue(self, obj):

View file

@ -173,7 +173,8 @@ class Date(Field):
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):
sdefault=None, scolspan=1, swidth=None, sheight=None,
persist=True):
self.format = format
self.calendar = calendar
self.startYear = startYear
@ -186,7 +187,7 @@ class Date(Field):
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, True, mapping, label, sdefault, scolspan,
swidth, sheight)
swidth, sheight, persist)
def getCss(self, layoutType, res):
# CSS files are only required if the calendar must be shown.

View file

@ -279,7 +279,7 @@ class File(Field):
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, True, mapping, label, sdefault, scolspan,
swidth, sheight)
swidth, sheight, True)
@staticmethod
def getFileObject(filePath, fileName=None, zope=False):

View file

@ -58,7 +58,7 @@ class Float(Field):
maxChars=13, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
sdefault=('',''), scolspan=1, swidth=None, sheight=None,
precision=None, sep=(',', '.'), tsep=' '):
persist=True, precision=None, sep=(',', '.'), tsep=' '):
# The precision is the number of decimal digits. This number is used
# for rendering the float, but the internal float representation is not
# rounded.
@ -80,7 +80,7 @@ class Float(Field):
specificReadPermission, specificWritePermission, width,
height, maxChars, colspan, master, masterValue, focus,
historized, True, mapping, label, sdefault, scolspan,
swidth, sheight)
swidth, sheight, persist)
self.pythonType = float
def getFormattedValue(self, obj, value, showChanges=False):

View file

@ -34,6 +34,6 @@ class Info(Field):
move, indexed, False, specificReadPermission,
specificWritePermission, width, height, None, colspan,
master, masterValue, focus, historized, False, mapping,
label, None, None, None, None)
label, None, None, None, None, False)
self.validable = False
# ------------------------------------------------------------------------------

View file

@ -54,13 +54,14 @@ class Integer(Field):
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):
sdefault=('',''), scolspan=1, swidth=None, sheight=None,
persist=True):
Field.__init__(self, validator, multiplicity, default, show, page,
group, layouts, move, indexed, searchable,
specificReadPermission, specificWritePermission, width,
height, maxChars, colspan, master, masterValue, focus,
historized, True, mapping, label, sdefault, scolspan,
swidth, sheight)
swidth, sheight, persist)
self.pythonType = long
def validateValue(self, obj, value):

View file

@ -83,7 +83,8 @@ class List(Field):
group, layouts, move, indexed, False,
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, True, mapping, label, None, None, None, None)
historized, True, mapping, label, None, None, None, None,
True)
self.validable = True
# Tuples of (names, Field instances) determining the format of every
# element in the list.

View file

@ -54,7 +54,7 @@ class Ogone(Field):
move, False, False,specificReadPermission,
specificWritePermission, width, height, None, colspan,
master, masterValue, focus, False, True, mapping, label,
None, None, None, None)
None, None, None, None, False)
# orderMethod must contain a method returning a dict containing info
# about the order. Following keys are mandatory:
# * orderID An identifier for the order. Don't use the object UID

View file

@ -84,7 +84,9 @@ class Pod(Field):
move, indexed, searchable, specificReadPermission,
specificWritePermission, width, height, None, colspan,
master, masterValue, focus, historized, False, mapping,
label, None, None, None, None)
label, None, None, None, None, True)
# Param "persist" is set to True but actually, persistence for a pod
# field is determined by freezing.
self.validable = False
def isFrozen(self, obj):

View file

@ -286,7 +286,7 @@ class Ref(Field):
uids=[o.UID() for o in \
field.getLinkedObjects(zobj).objects]"
name=":name" id=":name" size=":isMultiple and field.height or ''"
onchange=":field.getOnChange(name, zobj, layoutType)"
onchange=":field.getOnChange(zobj, layoutType)"
multiple=":isMultiple">
<option value="" if="not isMultiple">:_('choose_a_value')</option>
<option for="ztied in zobjects" var2="uid=ztied.o.UID()"
@ -328,8 +328,8 @@ class Ref(Field):
label=None, queryable=False, queryFields=None, queryNbCols=1,
navigable=False, searchSelect=None, changeOrder=True,
sdefault='', scolspan=1, swidth=None, sheight=None,
render='list', menuIdMethod=None, menuInfoMethod=None,
menuUrlMethod=None):
persist=True, render='list', menuIdMethod=None,
menuInfoMethod=None, menuUrlMethod=None):
self.klass = klass
self.attribute = attribute
# May the user add new objects through this ref ?
@ -431,7 +431,7 @@ class Ref(Field):
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
historized, sync, mapping, label, sdefault, scolspan,
swidth, sheight)
swidth, sheight, persist)
self.validable = self.link
def getDefaultLayouts(self):
@ -662,6 +662,7 @@ class Ref(Field):
* a Zope object;
* a Appy object;
* a list of Appy or Zope objects.'''
if not self.persist: return
# Standardize p_value into a list of Zope objects
objects = value
if not objects: objects = []
@ -759,6 +760,11 @@ class Ref(Field):
res = self.masterValue(obj, masterValues)
return res
else:
# If this field is a ajax-updatable slave, no need to compute
# selectable objects: it will be overridden by method
# self.masterValue by a subsequent ajax request (=the "if" statement
# above).
if self.masterValue and callable(self.masterValue): return []
if not self.select:
# No select method has been defined: we must retrieve all
# objects of the referred type that the user is allowed to

View file

@ -110,7 +110,7 @@ class String(Field):
withTranslations=True, withBlankValue=True)"
name=":name" id=":name" class=":masterCss"
multiple=":isMultiple and 'multiple' or ''"
onchange=":field.getOnChange(name, zobj, layoutType)"
onchange=":field.getOnChange(zobj, layoutType)"
size=":isMultiple and field.height or 1">
<option for="val in possibleValues" value=":val[0]"
selected=":field.isSelected(zobj, name, val[0], rawValue)"
@ -292,8 +292,9 @@ class String(Field):
width=None, height=None, maxChars=None, colspan=1, master=None,
masterValue=None, focus=False, historized=False, mapping=None,
label=None, sdefault='', scolspan=1, swidth=None, sheight=None,
transform='none', styles=('p','h1','h2','h3','h4'),
allowImageUpload=True, inlineEdit=False):
persist=True, transform='none',
styles=('p','h1','h2','h3','h4'), allowImageUpload=True,
inlineEdit=False):
# According to format, the widget will be different: input field,
# textarea, inline editor... Note that there can be only one String
# field of format CAPTCHA by page, because the captcha challenge is
@ -318,7 +319,7 @@ class String(Field):
specificReadPermission, specificWritePermission, width,
height, maxChars, colspan, master, masterValue, focus,
historized, True, mapping, label, sdefault, scolspan,
swidth, sheight)
swidth, sheight, persist)
self.isSelect = self.isSelection()
# If self.isSelect, self.sdefault must be a list of value(s).
if self.isSelect and not sdefault:
@ -390,6 +391,7 @@ class String(Field):
def store(self, obj, value):
'''When the value is XHTML, we perform some cleanup.'''
if not self.persist: return
if (self.format == String.XHTML) and value:
# When image upload is allowed, ckeditor inserts some "style" attrs
# (ie for image size when images are resized). So in this case we