[gen] Added field dict, similar to List but that stores dicts in the database, with a set of keys given in method field.keys.

This commit is contained in:
Gaetan Delannay 2015-03-16 12:08:13 +01:00
parent 424c0521de
commit 7e7d0933f5
7 changed files with 117 additions and 14 deletions

View file

@ -25,6 +25,9 @@ from group import Group
from search import Search
from page import Page
# In this file, names "list" and "dict" refer to sub-modules. To use Python
# builtin types, use __builtins__['list'] and __builtins__['dict']
# ------------------------------------------------------------------------------
class Field:
'''Basic abstract class for defining any field.'''
@ -396,7 +399,7 @@ class Field:
# We must initialise the corresponding back reference
self.back.klass = klass
self.back.init(self.back.attribute, self.klass, appName)
if self.type == "List":
if self.type in ('List', 'Dict'):
for subName, subField in self.fields:
fullName = '%s_%s' % (name, subName)
subField.init(fullName, klass, appName)
@ -490,7 +493,7 @@ class Field:
def formatMapping(self, mapping):
'''Creates a dict of mappings, one entry by label type (label, descr,
help).'''
if isinstance(mapping, dict):
if isinstance(mapping, __builtins__['dict']):
# Is it a dict like {'label':..., 'descr':...}, or is it directly a
# dict with a mapping?
for k, v in mapping.iteritems():
@ -504,7 +507,7 @@ class Field:
mapping[labelType] = None # No mapping for this value.
return mapping
else:
# Mapping is a method that must be applied to any i18n message.
# Mapping is a method that must be applied to any i18n message
return {'label':mapping, 'descr':mapping, 'help':mapping}
def formatLayouts(self, layouts):
@ -532,7 +535,7 @@ class Field:
layouts = copy.deepcopy(layouts)
if 'edit' not in layouts:
defEditLayout = self.computeDefaultLayouts()
if type(defEditLayout) == dict:
if isinstance(defEditLayout, __builtins__['dict']):
defEditLayout = defEditLayout['edit']
layouts['edit'] = defEditLayout
# We have now a dict of layouts in p_layouts. Ensure now that a Table

98
fields/dict.py Normal file
View file

@ -0,0 +1,98 @@
# ------------------------------------------------------------------------------
# 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 <http://www.gnu.org/licenses/>.
# ------------------------------------------------------------------------------
from appy import Object
from list import List
from appy.px import Px
from appy.gen.layout import Table
# ------------------------------------------------------------------------------
class Dict(List):
'''A Dict is stored as a dict of Object instances [Object]~. Keys are fixed
and are given by a method specified in parameter "keys". Values are
Object instances, whose attributes are determined by parameter "fields"
that, similarly to the List field, determines sub-data for every entry in
the dict. This field is build on top of the List field.'''
# PX for rendering a single row
pxRow = Px('''
<tr valign="top" class=":loop.row.odd and 'even' or 'odd'">
<td>:row[1]</td>
<td for="info in subFields" if="info[1]" align="center"
var2="field=info[1];
fieldName='%s*%d' % (field.name, rowIndex);
tagCss='noStyle'">:field.pxRender</td>
</tr>''')
# PX for rendering the dict (shared between pxView and pxEdit)
pxTable = Px('''
<table var="isEdit=layoutType == 'edit'" if="isEdit or value"
id=":'list_%s' % name" class=":isEdit and 'grid' or 'compact list'"
width=":field.width"
var2="keys=field.keys(obj);
subFields=field.getSubFields(zobj, layoutType)">
<!-- Header -->
<tr valign="bottom">
<th></th>
<th for="info in subFields" if="info[1]"
width=":field.widths[loop.info.nb]">::_(info[1].labelId)</th>
</tr>
<!-- Rows of data -->
<x for="row in keys" var2="rowIndex=loop.row.nb">:field.pxRow</x>
</table>''')
def __init__(self, keys, 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):
List.__init__(self, fields, validator, multiplicity, default, show, page,
group, layouts, move, specificReadPermission,
specificWritePermission, width, height, maxChars, colspan,
master, masterValue, focus, historized, mapping, label,
subLayouts, widths, view, xml)
# Method in "keys" must return a list of tuples (key, title): "key"
# determines the key that will be used to store the entry in the
# database, while "title" will get the text that will be shown in the ui
# while encoding/viewing this entry.
self.keys = keys
def getFormattedValue(self, obj, value, layoutType='view',
showChanges=False, language=None):
'''Formats the dict value as a list of values'''
res = []
for key, title in self.keys(obj.appy()):
if key in value:
res.append(value[key])
else:
# There is no value for this key in the database p_value
res.append(None)
return res
def getStorableValue(self, obj, value):
'''Gets p_value in a form that can be stored in the database'''
res = {}
values = List.getStorableValue(self, obj, value)
i = -1
for key, title in self.keys(obj.appy()):
i += 1
res[key] = values[i]
return res
# ------------------------------------------------------------------------------

View file

@ -123,14 +123,14 @@ class List(Field):
return res
def getRequestValue(self, obj, requestName=None):
'''Concatenates the list from distinct form elements in the request.'''
'''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
# I have found a row. Gets its index.
row = Object()
if '_' in key: key = key[:key.index('_')]
rowIndex = int(key.split('*')[-1])
@ -140,7 +140,7 @@ class List(Field):
v = subField.getRequestValue(obj, requestName=keyName)
setattr(row, subName, v)
res[rowIndex] = row
# Produce a sorted list.
# Produce a sorted list
keys = res.keys()
keys.sort()
res = [res[key] for key in keys]
@ -153,7 +153,7 @@ class List(Field):
return res
def getStorableValue(self, obj, value):
'''Gets p_value in a form that can be stored in the database.'''
'''Gets p_value in a form that can be stored in the database'''
res = []
for v in value:
sv = Object()