app/soc/views/helper/dynaform.py
changeset 602 1caee6675fa7
child 629 492df53e4a0f
equal deleted inserted replaced
601:988f8a8cd6de 602:1caee6675fa7
       
     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)