|
1 #!/usr/bin/python2.5 |
|
2 # |
|
3 # Copyright 2008 the Melange authors. |
|
4 # |
|
5 # Licensed under the Apache License, Version 2.0 (the "License"); |
|
6 # you may not use this file except in compliance with the License. |
|
7 # You may obtain a copy of the License at |
|
8 # |
|
9 # http://www.apache.org/licenses/LICENSE-2.0 |
|
10 # |
|
11 # Unless required by applicable law or agreed to in writing, software |
|
12 # distributed under the License is distributed on an "AS IS" BASIS, |
|
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
14 # See the License for the specific language governing permissions and |
|
15 # limitations under the License. |
|
16 |
|
17 """This module defines classes and functions for Dynamic Forms |
|
18 """ |
|
19 |
|
20 __authors__ = [ |
|
21 '"Sverre Rabbelier" <sverre@rabbelier.nl>', |
|
22 ] |
|
23 |
|
24 |
|
25 from google.appengine.ext.db import djangoforms |
|
26 |
|
27 from soc.logic import dicts |
|
28 |
|
29 |
|
30 class DynaFormMetaclass(djangoforms.ModelFormMetaclass): |
|
31 """The DynaForm Meta class, adding support for dynamically defined fields |
|
32 |
|
33 The new DynaForm class that is created by class function is very |
|
34 similar to one created by the regular djangoforms.ModelFormMetaclass. |
|
35 The only difference is that is the form class has a Meta property, |
|
36 it's 'dynaconf' field will be used to define additional properties |
|
37 in the form class. |
|
38 |
|
39 The 'dynaconf' field (if present), is expected to be iterable as a |
|
40 dictionary (with iteritems). The keys are used as the property names, |
|
41 and the values are used as the property value. |
|
42 """ |
|
43 |
|
44 def __new__(cls, class_name, bases, attrs): |
|
45 """See djangoforms.ModelFormMetaclass on how the __new__ method |
|
46 is used, for an explanation on how this class modifies the default |
|
47 behavior, see the DynaFormMetaclass's docstring. |
|
48 """ |
|
49 |
|
50 # Retrieve the Meta class, if present |
|
51 meta = attrs.get('Meta', None) |
|
52 conf = None |
|
53 |
|
54 # Try to retrieve the dynaconf property |
|
55 if meta: |
|
56 conf = getattr(meta, 'dynaconf', None) |
|
57 |
|
58 # When found, extend the form's attribute's with the specified ones |
|
59 if conf: |
|
60 for key, value in conf.iteritems(): |
|
61 attrs[key] = value |
|
62 |
|
63 # Leave the rest to djangoforms.ModelFormMetaclass. |
|
64 return super(DynaFormMetaclass, cls).__new__(cls, class_name, bases, attrs) |
|
65 |
|
66 |
|
67 def newDynaForm(dynamodel=None, dynabase=None, dynainclude=None, |
|
68 dynaexclude=None, dynafields=None): |
|
69 """Creates a new form DynaForm class |
|
70 |
|
71 The returned class extends dynabase, but with the following additions: |
|
72 * It has a Meta class with the 'model', 'include', and 'exclude' |
|
73 fields set as specified by newDynaForm's keyword arguments. |
|
74 * It's __metaclass__ is set to DynaFormMetaclass (which inherits from |
|
75 the default djangoforms.ModelFormMetaclass). |
|
76 * The Meta class has an additional dynaconf field which is set to |
|
77 the dyanfields keyword argument passed to newDynaForm. |
|
78 |
|
79 See DynaFormMetaclass for an explanation on how the dynafields |
|
80 property is used to construct the DynaForm class. |
|
81 """ |
|
82 |
|
83 class DynaForm(dynabase): |
|
84 """The dynamically created Form class |
|
85 """ |
|
86 |
|
87 __metaclass__ = DynaFormMetaclass |
|
88 |
|
89 class Meta: |
|
90 """Inner Meta class that defines some behavior for the form. |
|
91 """ |
|
92 |
|
93 model = dynamodel |
|
94 include = dynainclude |
|
95 exclude = dynaexclude |
|
96 dynaconf = dynafields |
|
97 |
|
98 return DynaForm |
|
99 |
|
100 |
|
101 def extendDynaForm(dynaform, dynainclude=None, dynaexclude=None, dynafields=None): |
|
102 """Extends an existing dynaform |
|
103 |
|
104 If any of dynainclude, dynaexclude or dynafields are not present, |
|
105 they are retrieved from dynaform (if present in it's Meta class). |
|
106 |
|
107 While it is rather useles to extend from a dynaform that does not have |
|
108 a Meta class, it is allowed, the resulting DynaForm is the same as if |
|
109 newDynaForm was called with all extendDynForm's keyword arguments. |
|
110 """ |
|
111 |
|
112 # Try to retrieve the Meta class from the existing dynaform |
|
113 meta = getattr(dynaform, 'Meta', None) |
|
114 |
|
115 # If we find one, we can use it to 'extend' from |
|
116 if meta: |
|
117 dynamodel = getattr(meta, 'model', None) |
|
118 |
|
119 if not dynainclude: |
|
120 dynainclude = getattr(meta, 'include', None) |
|
121 |
|
122 if not dynaexclude: |
|
123 dynaexclude = getattr(meta, 'exclude', None) |
|
124 |
|
125 # The most intersting parameter, the 'extra fields' dictionary |
|
126 dynaconf = getattr(meta, 'dynaconf', {}) |
|
127 if not dynafields: |
|
128 dynafields = dynaconf |
|
129 else: |
|
130 dicts.merge(dynafields, dynaconf) |
|
131 |
|
132 # Create a new DynaForm, using the properties we extracted |
|
133 return newDynaForm( |
|
134 dynamodel=dynamodel, |
|
135 dynabase=dynaform, |
|
136 dynainclude=dynainclude, |
|
137 dynaexclude=dynaexclude, |
|
138 dynafields=dynafields) |