# HG changeset patch # User Sverre Rabbelier # Date 1227912543 0 # Node ID 1caee6675fa7829b66b90acfdce40877b153c2da # Parent 988f8a8cd6de5de44a319be5fe02d5347d9460df Added a DynaForm module This allows GAE forms to be defined 'semi' dymamically, rather than by having to explicitly create the new class. Patch by: Sverre Rabbelier diff -r 988f8a8cd6de -r 1caee6675fa7 app/soc/views/helper/dynaform.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/soc/views/helper/dynaform.py Fri Nov 28 22:49:03 2008 +0000 @@ -0,0 +1,138 @@ +#!/usr/bin/python2.5 +# +# Copyright 2008 the Melange authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""This module defines classes and functions for Dynamic Forms +""" + +__authors__ = [ + '"Sverre Rabbelier" ', + ] + + +from google.appengine.ext.db import djangoforms + +from soc.logic import dicts + + +class DynaFormMetaclass(djangoforms.ModelFormMetaclass): + """The DynaForm Meta class, adding support for dynamically defined fields + + The new DynaForm class that is created by class function is very + similar to one created by the regular djangoforms.ModelFormMetaclass. + The only difference is that is the form class has a Meta property, + it's 'dynaconf' field will be used to define additional properties + in the form class. + + The 'dynaconf' field (if present), is expected to be iterable as a + dictionary (with iteritems). The keys are used as the property names, + and the values are used as the property value. + """ + + def __new__(cls, class_name, bases, attrs): + """See djangoforms.ModelFormMetaclass on how the __new__ method + is used, for an explanation on how this class modifies the default + behavior, see the DynaFormMetaclass's docstring. + """ + + # Retrieve the Meta class, if present + meta = attrs.get('Meta', None) + conf = None + + # Try to retrieve the dynaconf property + if meta: + conf = getattr(meta, 'dynaconf', None) + + # When found, extend the form's attribute's with the specified ones + if conf: + for key, value in conf.iteritems(): + attrs[key] = value + + # Leave the rest to djangoforms.ModelFormMetaclass. + return super(DynaFormMetaclass, cls).__new__(cls, class_name, bases, attrs) + + +def newDynaForm(dynamodel=None, dynabase=None, dynainclude=None, + dynaexclude=None, dynafields=None): + """Creates a new form DynaForm class + + The returned class extends dynabase, but with the following additions: + * It has a Meta class with the 'model', 'include', and 'exclude' + fields set as specified by newDynaForm's keyword arguments. + * It's __metaclass__ is set to DynaFormMetaclass (which inherits from + the default djangoforms.ModelFormMetaclass). + * The Meta class has an additional dynaconf field which is set to + the dyanfields keyword argument passed to newDynaForm. + + See DynaFormMetaclass for an explanation on how the dynafields + property is used to construct the DynaForm class. + """ + + class DynaForm(dynabase): + """The dynamically created Form class + """ + + __metaclass__ = DynaFormMetaclass + + class Meta: + """Inner Meta class that defines some behavior for the form. + """ + + model = dynamodel + include = dynainclude + exclude = dynaexclude + dynaconf = dynafields + + return DynaForm + + +def extendDynaForm(dynaform, dynainclude=None, dynaexclude=None, dynafields=None): + """Extends an existing dynaform + + If any of dynainclude, dynaexclude or dynafields are not present, + they are retrieved from dynaform (if present in it's Meta class). + + While it is rather useles to extend from a dynaform that does not have + a Meta class, it is allowed, the resulting DynaForm is the same as if + newDynaForm was called with all extendDynForm's keyword arguments. + """ + + # Try to retrieve the Meta class from the existing dynaform + meta = getattr(dynaform, 'Meta', None) + + # If we find one, we can use it to 'extend' from + if meta: + dynamodel = getattr(meta, 'model', None) + + if not dynainclude: + dynainclude = getattr(meta, 'include', None) + + if not dynaexclude: + dynaexclude = getattr(meta, 'exclude', None) + + # The most intersting parameter, the 'extra fields' dictionary + dynaconf = getattr(meta, 'dynaconf', {}) + if not dynafields: + dynafields = dynaconf + else: + dicts.merge(dynafields, dynaconf) + + # Create a new DynaForm, using the properties we extracted + return newDynaForm( + dynamodel=dynamodel, + dynabase=dynaform, + dynainclude=dynainclude, + dynaexclude=dynaexclude, + dynafields=dynafields)