[fields] computed.py: plainText is now False by default, method can now be a PX [fields] list.py: bugfixes in the validation process; [gen] within aby PX, its context is now available as a special var '_ctx_': to use with caution only for the needs of Appy itself. It is not meant to be used by Appy developers.
This commit is contained in:
parent
59dc619c7f
commit
6206dbe59c
|
@ -555,7 +555,7 @@ class Field:
|
||||||
representation of the field value coming from the request.
|
representation of the field value coming from the request.
|
||||||
This method computes the real (potentially converted or manipulated
|
This method computes the real (potentially converted or manipulated
|
||||||
in some other way) value as can be stored in the database.'''
|
in some other way) value as can be stored in the database.'''
|
||||||
if self.isEmptyValue(value): return None
|
if self.isEmptyValue(value): return
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def getMasterData(self):
|
def getMasterData(self):
|
||||||
|
@ -572,7 +572,7 @@ class Field:
|
||||||
'''This method may be overridden by child classes and will be called at
|
'''This method may be overridden by child classes and will be called at
|
||||||
the right moment by m_validate defined below for triggering
|
the right moment by m_validate defined below for triggering
|
||||||
type-specific validation. p_value is never empty.'''
|
type-specific validation. p_value is never empty.'''
|
||||||
return None
|
return
|
||||||
|
|
||||||
def securityCheck(self, obj, value):
|
def securityCheck(self, obj, value):
|
||||||
'''This method performs some security checks on the p_value that
|
'''This method performs some security checks on the p_value that
|
||||||
|
|
|
@ -46,21 +46,18 @@ class Computed(Field):
|
||||||
show='view', page='main', group=None, layouts=None, move=0,
|
show='view', page='main', group=None, layouts=None, move=0,
|
||||||
indexed=False, searchable=False, specificReadPermission=False,
|
indexed=False, searchable=False, specificReadPermission=False,
|
||||||
specificWritePermission=False, width=None, height=None,
|
specificWritePermission=False, width=None, height=None,
|
||||||
maxChars=None, colspan=1, method=None, plainText=True,
|
maxChars=None, colspan=1, method=None, plainText=False,
|
||||||
master=None, masterValue=None, focus=False, historized=False,
|
master=None, masterValue=None, focus=False, historized=False,
|
||||||
sync=True, mapping=None, label=None, sdefault='', scolspan=1,
|
sync=True, mapping=None, label=None, sdefault='', scolspan=1,
|
||||||
swidth=None, sheight=None, context={}):
|
swidth=None, sheight=None, context={}):
|
||||||
# The Python method used for computing the field value
|
# The Python method used for computing the field value, or a PX.
|
||||||
self.method = method
|
self.method = method
|
||||||
# Does field computation produce plain text or XHTML?
|
# Does field computation produce plain text or XHTML?
|
||||||
self.plainText = plainText
|
self.plainText = plainText
|
||||||
if isinstance(method, basestring):
|
if isinstance(method, Px):
|
||||||
# When field computation is done with a macro, we know the result
|
# When field computation is done with a PX, the result is XHTML.
|
||||||
# will be HTML.
|
|
||||||
self.plainText = False
|
self.plainText = False
|
||||||
# The context is a dict (or method returning a dict) that will be given
|
# If method is a PX, its context can be given in p_context.
|
||||||
# to the macro specified in self.method. If the dict contains key
|
|
||||||
# "someKey", it will be available to the macro as "options/someKey".
|
|
||||||
self.context = context
|
self.context = context
|
||||||
Field.__init__(self, None, multiplicity, default, show, page, group,
|
Field.__init__(self, None, multiplicity, default, show, page, group,
|
||||||
layouts, move, indexed, searchable,
|
layouts, move, indexed, searchable,
|
||||||
|
@ -70,33 +67,15 @@ class Computed(Field):
|
||||||
swidth, sheight)
|
swidth, sheight)
|
||||||
self.validable = False
|
self.validable = False
|
||||||
|
|
||||||
def callMacro(self, obj, macroPath):
|
|
||||||
'''Returns the macro corresponding to p_macroPath. The base folder
|
|
||||||
where we search is "ui".'''
|
|
||||||
# Get the special page in Appy that allows to call a macro
|
|
||||||
macroPage = obj.ui.callMacro
|
|
||||||
# Get, from p_macroPath, the page where the macro lies, and the macro
|
|
||||||
# name.
|
|
||||||
names = self.method.split('/')
|
|
||||||
# Get the page where the macro lies
|
|
||||||
page = obj.ui
|
|
||||||
for name in names[:-1]:
|
|
||||||
page = getattr(page, name)
|
|
||||||
macroName = names[-1]
|
|
||||||
# Compute the macro context.
|
|
||||||
ctx = {'contextObj':obj, 'page':page, 'macroName':macroName}
|
|
||||||
if callable(self.context):
|
|
||||||
ctx.update(self.context(obj.appy()))
|
|
||||||
else:
|
|
||||||
ctx.update(self.context)
|
|
||||||
return macroPage(obj, **ctx)
|
|
||||||
|
|
||||||
def getValue(self, obj):
|
def getValue(self, obj):
|
||||||
'''Computes the value instead of getting it in the database.'''
|
'''Computes the value instead of getting it in the database.'''
|
||||||
if not self.method: return
|
if not self.method: return
|
||||||
if isinstance(self.method, basestring):
|
if isinstance(self.method, Px):
|
||||||
# self.method is a path to a macro that will produce the field value
|
obj = obj.appy()
|
||||||
return self.callMacro(obj, self.method)
|
ctx = {'obj': obj, 'field': self,
|
||||||
|
'_': obj.translate, 'tool': obj.tool}
|
||||||
|
ctx.update(self.context)
|
||||||
|
return self.method(ctx)
|
||||||
else:
|
else:
|
||||||
# self.method is a method that will return the field value
|
# self.method is a method that will return the field value
|
||||||
return self.callMethod(obj, self.method, cache=False)
|
return self.callMethod(obj, self.method, cache=False)
|
||||||
|
|
|
@ -140,17 +140,30 @@ class List(Field):
|
||||||
for v in value:
|
for v in value:
|
||||||
sv = Object()
|
sv = Object()
|
||||||
for name, field in self.fields:
|
for name, field in self.fields:
|
||||||
setattr(sv, name, field.getStorableValue(getattr(v, name)))
|
subValue = getattr(v, name)
|
||||||
|
try:
|
||||||
|
setattr(sv, name, field.getStorableValue(subValue))
|
||||||
|
except ValueError:
|
||||||
|
# The value for this field for this specific row is
|
||||||
|
# incorrect. It can happen in the process of validating the
|
||||||
|
# whole List field (a call to getStorableValue occurs at
|
||||||
|
# this time). We don't care about it, because later on we
|
||||||
|
# will have sub-field specific validation that will also
|
||||||
|
# detect the error and will prevent storing the wrong value
|
||||||
|
# in the database.
|
||||||
|
setattr(sv, name, subValue)
|
||||||
res.append(sv)
|
res.append(sv)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getInnerValue(self, outerValue, name, i):
|
def getInnerValue(self, obj, outerValue, name, i):
|
||||||
'''Returns the value of inner field named p_name in row number p_i
|
'''Returns the value of inner field named p_name in row number p_i
|
||||||
within the whole list of values p_outerValue.'''
|
within the whole list of values p_outerValue.'''
|
||||||
if i == -1: return ''
|
if i == -1: return ''
|
||||||
if not outerValue: return ''
|
if not outerValue: return ''
|
||||||
if i >= len(outerValue): return ''
|
if i >= len(outerValue): return ''
|
||||||
return getattr(outerValue[i], name, '')
|
# Return the value, or a potential default value.
|
||||||
|
return getattr(outerValue[i], name, '') or \
|
||||||
|
self.getField(name).getValue(obj) or ''
|
||||||
|
|
||||||
def getCss(self, layoutType, res):
|
def getCss(self, layoutType, res):
|
||||||
'''Gets the CSS required by sub-fields if any.'''
|
'''Gets the CSS required by sub-fields if any.'''
|
||||||
|
|
|
@ -300,23 +300,23 @@ class BaseMixin:
|
||||||
field, we add in p_values an entry with the "ready-to-store" field
|
field, we add in p_values an entry with the "ready-to-store" field
|
||||||
value.'''
|
value.'''
|
||||||
rq = self.REQUEST
|
rq = self.REQUEST
|
||||||
for appyType in self.getAppyTypes('edit', rq.form.get('page')):
|
for field in self.getAppyTypes('edit', rq.form.get('page')):
|
||||||
if not appyType.validable: continue
|
if not field.validable: continue
|
||||||
value = appyType.getRequestValue(rq)
|
value = field.getRequestValue(rq)
|
||||||
message = appyType.validate(self, value)
|
message = field.validate(self, value)
|
||||||
if message:
|
if message:
|
||||||
setattr(errors, appyType.name, message)
|
setattr(errors, field.name, message)
|
||||||
else:
|
else:
|
||||||
setattr(values, appyType.name, appyType.getStorableValue(value))
|
setattr(values, field.name, field.getStorableValue(value))
|
||||||
# Validate sub-fields within Lists
|
# Validate sub-fields within Lists
|
||||||
if appyType.type != 'List': continue
|
if field.type != 'List': continue
|
||||||
i = -1
|
i = -1
|
||||||
for row in value:
|
for row in value:
|
||||||
i += 1
|
i += 1
|
||||||
for name, field in appyType.fields:
|
for name, subField in field.fields:
|
||||||
message = field.validate(self, getattr(row,name,None))
|
message = subField.validate(self, getattr(row,name,None))
|
||||||
if message:
|
if message:
|
||||||
setattr(errors, '%s*%d' % (field.name, i), message)
|
setattr(errors, '%s*%d' % (subField.name, i), message)
|
||||||
|
|
||||||
def interFieldValidation(self, errors, values):
|
def interFieldValidation(self, errors, values):
|
||||||
'''This method is called when individual validation of all fields
|
'''This method is called when individual validation of all fields
|
||||||
|
@ -638,14 +638,14 @@ class BaseMixin:
|
||||||
'''Returns the database value of field named p_name for p_self.
|
'''Returns the database value of field named p_name for p_self.
|
||||||
If p_onlyIfSync is True, it returns the value only if appyType can be
|
If p_onlyIfSync is True, it returns the value only if appyType can be
|
||||||
retrieved in synchronous mode.'''
|
retrieved in synchronous mode.'''
|
||||||
appyType = self.getAppyType(name)
|
field = self.getAppyType(name)
|
||||||
if not onlyIfSync or (onlyIfSync and appyType.sync[layoutType]):
|
if not onlyIfSync or (onlyIfSync and field.sync[layoutType]):
|
||||||
# We must really get the field value.
|
# We must really get the field value.
|
||||||
if '*' not in name: return appyType.getValue(self)
|
if '*' not in name: return field.getValue(self)
|
||||||
# The field is an inner field from a List.
|
# The field is an inner field from a List.
|
||||||
listName, name, i = name.split('*')
|
listName, name, i = name.split('*')
|
||||||
listType = self.getAppyType(listName)
|
listType = self.getAppyType(listName)
|
||||||
return listType.getInnerValue(outerValue, name, int(i))
|
return listType.getInnerValue(self, outerValue, name, int(i))
|
||||||
|
|
||||||
def getFormattedFieldValue(self, name, value, showChanges=False):
|
def getFormattedFieldValue(self, name, value, showChanges=False):
|
||||||
'''Gets a nice, string representation of p_value which is a value from
|
'''Gets a nice, string representation of p_value which is a value from
|
||||||
|
|
|
@ -88,6 +88,7 @@ class AbstractWrapper(object):
|
||||||
appFolder=app.data; url = ztool.getIncludeUrl;
|
appFolder=app.data; url = ztool.getIncludeUrl;
|
||||||
appName=ztool.getAppName(); _=ztool.translate;
|
appName=ztool.getAppName(); _=ztool.translate;
|
||||||
req=ztool.REQUEST; resp=req.RESPONSE;
|
req=ztool.REQUEST; resp=req.RESPONSE;
|
||||||
|
dummy=setattr(req, 'pxContext', _ctx_);
|
||||||
lang=ztool.getUserLanguage(); q=ztool.quote;
|
lang=ztool.getUserLanguage(); q=ztool.quote;
|
||||||
layoutType=ztool.getLayoutType();
|
layoutType=ztool.getLayoutType();
|
||||||
showPortlet=ztool.showPortlet(zobj, layoutType);
|
showPortlet=ztool.showPortlet(zobj, layoutType);
|
||||||
|
@ -589,6 +590,7 @@ class AbstractWrapper(object):
|
||||||
appFolder=app.data; url = ztool.getIncludeUrl;
|
appFolder=app.data; url = ztool.getIncludeUrl;
|
||||||
appName=ztool.getAppName(); _=ztool.translate;
|
appName=ztool.getAppName(); _=ztool.translate;
|
||||||
req=ztool.REQUEST; resp=req.RESPONSE;
|
req=ztool.REQUEST; resp=req.RESPONSE;
|
||||||
|
dummy=setattr(req, 'pxContext', _ctx_);
|
||||||
lang=ztool.getUserLanguage(); q=ztool.quote;
|
lang=ztool.getUserLanguage(); q=ztool.quote;
|
||||||
action=req.get('action', None);
|
action=req.get('action', None);
|
||||||
px=req['px'].split(':');
|
px=req['px'].split(':');
|
||||||
|
|
|
@ -90,6 +90,9 @@ class Px:
|
||||||
as is, without re-applying the template (else, an infinite
|
as is, without re-applying the template (else, an infinite
|
||||||
recursion would occur).
|
recursion would occur).
|
||||||
'''
|
'''
|
||||||
|
# Developer, forget the following line forever.
|
||||||
|
if '_ctx_' not in context: context['_ctx_'] = context
|
||||||
|
|
||||||
if self.hook and applyTemplate:
|
if self.hook and applyTemplate:
|
||||||
# Call the template PX, filling the hook with the current PX.
|
# Call the template PX, filling the hook with the current PX.
|
||||||
context[self.hook] = self
|
context[self.hook] = self
|
||||||
|
|
Loading…
Reference in a new issue