[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
					
				
					 6 changed files with 48 additions and 51 deletions
				
			
		| 
						 | 
				
			
			@ -555,7 +555,7 @@ class Field:
 | 
			
		|||
           representation of the field value coming from the request.
 | 
			
		||||
           This method computes the real (potentially converted or manipulated
 | 
			
		||||
           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
 | 
			
		||||
 | 
			
		||||
    def getMasterData(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -572,7 +572,7 @@ class Field:
 | 
			
		|||
        '''This method may be overridden by child classes and will be called at
 | 
			
		||||
           the right moment by m_validate defined below for triggering
 | 
			
		||||
           type-specific validation. p_value is never empty.'''
 | 
			
		||||
        return None
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    def securityCheck(self, obj, value):
 | 
			
		||||
        '''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,
 | 
			
		||||
                 indexed=False, searchable=False, specificReadPermission=False,
 | 
			
		||||
                 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,
 | 
			
		||||
                 sync=True, mapping=None, label=None, sdefault='', scolspan=1,
 | 
			
		||||
                 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
 | 
			
		||||
        # Does field computation produce plain text or XHTML?
 | 
			
		||||
        self.plainText = plainText
 | 
			
		||||
        if isinstance(method, basestring):
 | 
			
		||||
            # When field computation is done with a macro, we know the result
 | 
			
		||||
            # will be HTML.
 | 
			
		||||
        if isinstance(method, Px):
 | 
			
		||||
            # When field computation is done with a PX, the result is XHTML.
 | 
			
		||||
            self.plainText = False
 | 
			
		||||
        # The context is a dict (or method returning a dict) that will be given
 | 
			
		||||
        # to the macro specified in self.method. If the dict contains key
 | 
			
		||||
        # "someKey", it will be available to the macro as "options/someKey".
 | 
			
		||||
        # 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,
 | 
			
		||||
| 
						 | 
				
			
			@ -70,33 +67,15 @@ class Computed(Field):
 | 
			
		|||
                       swidth, sheight)
 | 
			
		||||
        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):
 | 
			
		||||
        '''Computes the value instead of getting it in the database.'''
 | 
			
		||||
        if not self.method: return
 | 
			
		||||
        if isinstance(self.method, basestring):
 | 
			
		||||
            # self.method is a path to a macro that will produce the field value
 | 
			
		||||
            return self.callMacro(obj, self.method)
 | 
			
		||||
        if isinstance(self.method, Px):
 | 
			
		||||
            obj = obj.appy()
 | 
			
		||||
            ctx = {'obj': obj, 'field': self,
 | 
			
		||||
                   '_': obj.translate, 'tool': obj.tool}
 | 
			
		||||
            ctx.update(self.context)
 | 
			
		||||
            return self.method(ctx)
 | 
			
		||||
        else:
 | 
			
		||||
            # self.method is a method that will return the field value
 | 
			
		||||
            return self.callMethod(obj, self.method, cache=False)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -140,17 +140,30 @@ class List(Field):
 | 
			
		|||
        for v in value:
 | 
			
		||||
            sv = Object()
 | 
			
		||||
            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)
 | 
			
		||||
        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
 | 
			
		||||
           within the whole list of values p_outerValue.'''
 | 
			
		||||
        if i == -1: return ''
 | 
			
		||||
        if not 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):
 | 
			
		||||
        '''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
 | 
			
		||||
           value.'''
 | 
			
		||||
        rq = self.REQUEST
 | 
			
		||||
        for appyType in self.getAppyTypes('edit', rq.form.get('page')):
 | 
			
		||||
            if not appyType.validable: continue
 | 
			
		||||
            value = appyType.getRequestValue(rq)
 | 
			
		||||
            message = appyType.validate(self, value)
 | 
			
		||||
        for field in self.getAppyTypes('edit', rq.form.get('page')):
 | 
			
		||||
            if not field.validable: continue
 | 
			
		||||
            value = field.getRequestValue(rq)
 | 
			
		||||
            message = field.validate(self, value)
 | 
			
		||||
            if message:
 | 
			
		||||
                setattr(errors, appyType.name, message)
 | 
			
		||||
                setattr(errors, field.name, message)
 | 
			
		||||
            else:
 | 
			
		||||
                setattr(values, appyType.name, appyType.getStorableValue(value))
 | 
			
		||||
                setattr(values, field.name, field.getStorableValue(value))
 | 
			
		||||
            # Validate sub-fields within Lists
 | 
			
		||||
            if appyType.type != 'List': continue
 | 
			
		||||
            if field.type != 'List': continue
 | 
			
		||||
            i = -1
 | 
			
		||||
            for row in value:
 | 
			
		||||
                i += 1
 | 
			
		||||
                for name, field in appyType.fields:
 | 
			
		||||
                    message = field.validate(self, getattr(row,name,None))
 | 
			
		||||
                for name, subField in field.fields:
 | 
			
		||||
                    message = subField.validate(self, getattr(row,name,None))
 | 
			
		||||
                    if message:
 | 
			
		||||
                        setattr(errors, '%s*%d' % (field.name, i), message)
 | 
			
		||||
                        setattr(errors, '%s*%d' % (subField.name, i), message)
 | 
			
		||||
 | 
			
		||||
    def interFieldValidation(self, errors, values):
 | 
			
		||||
        '''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.
 | 
			
		||||
           If p_onlyIfSync is True, it returns the value only if appyType can be
 | 
			
		||||
           retrieved in synchronous mode.'''
 | 
			
		||||
        appyType = self.getAppyType(name)
 | 
			
		||||
        if not onlyIfSync or (onlyIfSync and appyType.sync[layoutType]):
 | 
			
		||||
        field = self.getAppyType(name)
 | 
			
		||||
        if not onlyIfSync or (onlyIfSync and field.sync[layoutType]):
 | 
			
		||||
            # 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.
 | 
			
		||||
            listName, name, i = name.split('*')
 | 
			
		||||
            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):
 | 
			
		||||
        '''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;
 | 
			
		||||
                appName=ztool.getAppName();     _=ztool.translate;
 | 
			
		||||
                req=ztool.REQUEST;              resp=req.RESPONSE;
 | 
			
		||||
                dummy=setattr(req, 'pxContext', _ctx_);
 | 
			
		||||
                lang=ztool.getUserLanguage();   q=ztool.quote;
 | 
			
		||||
                layoutType=ztool.getLayoutType();
 | 
			
		||||
                showPortlet=ztool.showPortlet(zobj, layoutType);
 | 
			
		||||
| 
						 | 
				
			
			@ -589,6 +590,7 @@ class AbstractWrapper(object):
 | 
			
		|||
             appFolder=app.data;             url = ztool.getIncludeUrl;
 | 
			
		||||
             appName=ztool.getAppName();     _=ztool.translate;
 | 
			
		||||
             req=ztool.REQUEST;              resp=req.RESPONSE;
 | 
			
		||||
             dummy=setattr(req, 'pxContext', _ctx_);
 | 
			
		||||
             lang=ztool.getUserLanguage();   q=ztool.quote;
 | 
			
		||||
             action=req.get('action', None);
 | 
			
		||||
             px=req['px'].split(':');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -90,6 +90,9 @@ class Px:
 | 
			
		|||
              as is, without re-applying the template (else, an infinite
 | 
			
		||||
              recursion would occur).
 | 
			
		||||
        '''
 | 
			
		||||
        # Developer, forget the following line forever.
 | 
			
		||||
        if '_ctx_' not in context: context['_ctx_'] = context
 | 
			
		||||
 | 
			
		||||
        if self.hook and applyTemplate:
 | 
			
		||||
            # Call the template PX, filling the hook with the current PX.
 | 
			
		||||
            context[self.hook] = self
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue