# ------------------------------------------------------------------------------ # This file is part of Appy, a framework for building applications in the Python # language. Copyright (C) 2007 Gaetan Delannay # Appy is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation; either version 3 of the License, or (at your option) any later # version. # Appy is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU General Public License for more details. # You should have received a copy of the GNU General Public License along with # Appy. If not, see . # ------------------------------------------------------------------------------ from appy import Object from appy.fields import Field from appy.px import Px from appy.gen.layout import Table # ------------------------------------------------------------------------------ class List(Field): '''A list, stored as a list of Object instances ~[Object]~. Every object in the list has attributes named according to the sub-fields defined in this List.''' # PX for rendering a single row pxRow = Px(''' :field.pxRender ''') # PX for rendering the list (shared between pxView and pxEdit) pxTable = Px(''' :field.pxRow:field.pxRow
::_(info[1].labelId)
''') pxView = pxCell = Px(''':field.pxTable''') pxEdit = Px(''' :field.pxTable ''') pxSearch = '' def __init__(self, fields, validator=None, multiplicity=(0,1), default=None, show=True, page='main', group=None, layouts=None, move=0, 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, False, True, False, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, mapping, label, None, None, None, None, True, view, xml) self.validable = True # Tuples of (names, Field instances) determining the format of every # element in the list. self.fields = fields # Force some layouting for sub-fields, if subLayouts are given. So the # one who wants freedom on tuning layouts at the field level must # specify subLayouts=None. if subLayouts: for name, field in self.fields: field.layouts = field.formatLayouts(subLayouts) # One may specify the width of every column in the list. Indeed, using # widths and layouts of sub-fields may not be sufficient. if not widths: self.widths = [''] * len(self.fields) else: self.widths = widths def getField(self, name): '''Gets the field definition whose name is p_name.''' for n, field in self.fields: if n == name: return field def getSubFields(self, obj, layoutType): '''Returns the sub-fields (name, Field) that are showable among field.fields on the given p_layoutType. Fields that cannot appear in the result are nevertheless present as a tuple (name, None). This way, it keeps a nice layouting of the table.''' res = [] for n, field in self.fields: elem = (n, None) if field.isShowable(obj, layoutType): elem = (n, field) res.append(elem) return res def getRequestValue(self, obj, requestName=None): '''Concatenates the list from distinct form elements in the request''' request = obj.REQUEST name = requestName or self.name # A List may be into another List (?) prefix = name + '*' + self.fields[0][0] + '*' res = {} for key in request.keys(): if not key.startswith(prefix): continue # I have found a row. Gets its index. row = Object() if '_' in key: key = key[:key.index('_')] rowIndex = int(key.split('*')[-1]) if rowIndex == -1: continue # Ignore the template row. for subName, subField in self.fields: keyName = '%s*%s*%s' % (name, subName, rowIndex) v = subField.getRequestValue(obj, requestName=keyName) setattr(row, subName, v) res[rowIndex] = row # Produce a sorted list keys = res.keys() keys.sort() res = [res[key] for key in keys] # I store in the request this computed value. This way, when individual # subFields will need to get their value, they will take it from here, # instead of taking it from the specific request key. Indeed, specific # request keys contain row indexes that may be wrong after row deletions # by the user. request.set(name, res) return res def getStorableValue(self, obj, value): '''Gets p_value in a form that can be stored in the database''' res = [] for v in value: sv = Object() for name, field in self.fields: subValue = getattr(v, name) try: setattr(sv, name, field.getStorableValue(obj, 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 m_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, 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 the value, or a potential default value value = getattr(outerValue[i], name, None) if value != None: return value value = self.getField(name).getValue(obj) if value != None: return value return '' def getCss(self, layoutType, res, config): '''Gets the CSS required by sub-fields if any''' for name, field in self.fields: field.getCss(layoutType, res, config) def getJs(self, layoutType, res, config): '''Gets the JS required by sub-fields if any''' for name, field in self.fields: field.getJs(layoutType, res, config) # ------------------------------------------------------------------------------