# ------------------------------------------------------------------------------
# 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.'''
# PX for rendering a single row.
pxRow = Px('''
:field.pxRender
''')
# PX for rendering the list (shared between pxView and pxEdit).
pxTable = Px('''
::_(info[1].labelId)
:field.pxRow
:field.pxRow
''')
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,
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):
Field.__init__(self, validator, multiplicity, default, show, page,
group, layouts, move, indexed, False,
specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus,
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.
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, request, requestName=None):
'''Concatenates the list from distinct form elements in the 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(request, 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, 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(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, 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.
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.'''
for name, field in self.fields:
field.getCss(layoutType, res)
def getJs(self, layoutType, res):
'''Gets the JS required by sub-fields if any.'''
for name, field in self.fields:
field.getJs(layoutType, res)
# ------------------------------------------------------------------------------