[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:
parent
424c0521de
commit
7e7d0933f5
|
@ -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
98
fields/dict.py
Normal 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
|
||||
# ------------------------------------------------------------------------------
|
|
@ -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()
|
||||
|
|
|
@ -29,6 +29,7 @@ from appy.fields.float import Float
|
|||
from appy.fields.info import Info
|
||||
from appy.fields.integer import Integer
|
||||
from appy.fields.list import List
|
||||
from appy.fields.dict import Dict
|
||||
from appy.fields.pod import Pod
|
||||
from appy.fields.ref import Ref, autoref
|
||||
from appy.fields.string import String, Selection
|
||||
|
|
|
@ -319,6 +319,7 @@ class FieldDescriptor:
|
|||
self.i18n('%s_descr' % label, ' ')
|
||||
if field.hasHelp:
|
||||
self.i18n('%s_help' % label, ' ')
|
||||
walkDict = walkList # Same i18n labels for a dict
|
||||
|
||||
def walkCalendar(self):
|
||||
# Add i18n-specific messages
|
||||
|
|
|
@ -256,12 +256,12 @@ class BaseMixin:
|
|||
return dbFolder, path
|
||||
|
||||
def view(self):
|
||||
'''Returns the view PX.'''
|
||||
'''Returns the view PX'''
|
||||
obj = self.appy()
|
||||
return obj.pxView({'obj': obj, 'tool': obj.tool})
|
||||
|
||||
def edit(self):
|
||||
'''Returns the edit PX.'''
|
||||
'''Returns the edit PX'''
|
||||
obj = self.appy()
|
||||
return obj.pxEdit({'obj': obj, 'tool': obj.tool})
|
||||
|
||||
|
@ -347,8 +347,8 @@ class BaseMixin:
|
|||
setattr(errors, field.name, message)
|
||||
else:
|
||||
setattr(values, field.name, field.getStorableValue(self, value))
|
||||
# Validate sub-fields within Lists
|
||||
if field.type != 'List': continue
|
||||
# Validate sub-fields within Lists/Dicts
|
||||
if field.type not in ('List', 'Dict'): continue
|
||||
i = -1
|
||||
for row in value:
|
||||
i += 1
|
||||
|
@ -710,7 +710,7 @@ class BaseMixin:
|
|||
field = self.getAppyType(name)
|
||||
if field.type == 'Pod': return
|
||||
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/Dict
|
||||
listName, name, i = name.split('*')
|
||||
listType = self.getAppyType(listName)
|
||||
return listType.getInnerValue(self, outerValue, name, int(i))
|
||||
|
|
|
@ -124,7 +124,7 @@ td.search { padding-top: 8px }
|
|||
.compact th, .compact td { padding: 0px 3px 0px 3px }
|
||||
.grid th { font-style: italic; font-weight: normal;
|
||||
border-bottom: 5px solid #fdfdfd; padding: 3px 5px 0 5px }
|
||||
.grid td { padding: 3px 3px 0 3px }
|
||||
.grid td { padding: 0 3px }
|
||||
.timeline { font-size: 85%; color: #555555 }
|
||||
.timeline td { text-align: center; padding: 1px }
|
||||
.timeline th { padding: 1px }
|
||||
|
|
Loading…
Reference in a new issue