Package couchdbkit :: Package ext :: Package django :: Module forms
[hide private]
[frames] | no frames]

Source Code for Module couchdbkit.ext.django.forms

  1  # -*- coding: utf-8 -*- 
  2  # 
  3  # Copyright (c) 2008-2009 Benoit Chesneau <benoitc@e-engura.com>  
  4  # 
  5  # Permission to use, copy, modify, and distribute this software for any 
  6  # purpose with or without fee is hereby granted, provided that the above 
  7  # copyright notice and this permission notice appear in all copies. 
  8  # 
  9  # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 
 10  # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 
 11  # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 
 12  # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 
 13  # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
 14  # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
 15  # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 
 16  # 
 17  # code heavily inspired from django.forms.models 
 18  # Copyright (c) Django Software Foundation and individual contributors. 
 19  # All rights reserved. 
 20  #  
 21  # Redistribution and use in source and binary forms, with or without modification, 
 22  # are permitted provided that the following conditions are met: 
 23  # 
 24  #    1. Redistributions of source code must retain the above copyright notice,  
 25  #       this list of conditions and the following disclaimer. 
 26  #     
 27  #    2. Redistributions in binary form must reproduce the above copyright  
 28  #       notice, this list of conditions and the following disclaimer in the 
 29  #       documentation and/or other materials provided with the distribution. 
 30  # 
 31  #    3. Neither the name of Django nor the names of its contributors may be used 
 32  #       to endorse or promote products derived from this software without 
 33  #       specific prior written permission. 
 34  # 
 35  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
 36  # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 37  # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 38  # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
 39  # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 40  # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 41  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
 42  # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 43  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 44  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 45   
 46  """ Implement DocumentForm object. It map Document objects to Form and  
 47  works like ModelForm object : 
 48   
 49      >>> from couchdbkit.ext.django.forms  import DocumentForm 
 50   
 51      # Create the form class. 
 52      >>> class ArticleForm(DocumentForm): 
 53      ...     class Meta: 
 54      ...         model = Article 
 55   
 56      # Creating a form to add an article. 
 57      >>> form = ArticleForm() 
 58   
 59      # Creating a form to change an existing article. 
 60      >>> article = Article.get(someid) 
 61      >>> form = ArticleForm(instance=article) 
 62       
 63   
 64  The generated Form class will have a form field for every model field.  
 65  Each document property has a corresponding default form field: 
 66   
 67  * StringProperty   ->  CharField, 
 68  * IntegerProperty  ->  IntegerField, 
 69  * DecimalProperty  ->  DecimalField, 
 70  * BooleanProperty  ->  BooleanField, 
 71  * FloatProperty    ->  FloatField, 
 72  * DateTimeProperty ->  DateTimeField, 
 73  * DateProperty     ->  DateField, 
 74  * TimeProperty     ->  TimeField 
 75   
 76   
 77  More fields types will be supported soon. 
 78  """ 
 79   
 80   
 81  from django.utils.text import capfirst 
 82  from django.utils.datastructures import SortedDict 
 83  from django.forms.util import ValidationError, ErrorList 
 84  from django.forms.forms import BaseForm, get_declared_fields 
 85  from django.forms import fields as f 
 86  from django.forms.widgets import media_property 
 87   
 88  from . import schema 
 89  from ...schema import value_to_python 
 90   
 91  FIELDS_PROPERTES_MAPPING = { 
 92      "StringProperty": f.CharField, 
 93      "IntegerProperty": f.IntegerField, 
 94      "DecimalProperty": f.DecimalField, 
 95      "BooleanProperty": f.BooleanField, 
 96      "FloatProperty": f.FloatField, 
 97      "DateTimeProperty": f.DateTimeField, 
 98      "DateProperty": f.DateField, 
 99      "TimeProperty": f.TimeField 
100  } 
101   
102 -def document_to_dict(instance, properties=None, exclude=None):
103 """ 104 Returns a dict containing the data in ``instance`` suitable for passing as 105 a Form's ``initial`` keyword argument. 106 107 ``properties`` is an optional list of properties names. If provided, 108 only the named properties will be included in the returned dict. 109 110 ``exclude`` is an optional list of properties names. If provided, the named 111 properties will be excluded from the returned dict, even if they are listed 112 in the ``properties`` argument. 113 """ 114 # avoid a circular import 115 data = {} 116 for prop_name in instance._doc.keys(): 117 if properties and not prop_name in properties: 118 continue 119 if exclude and prop_name in exclude: 120 continue 121 data[prop_name] = instance[prop_name] 122 return data
123
124 -def fields_for_document(document, properties=None, exclude=None):
125 """ 126 Returns a ``SortedDict`` containing form fields for the given document. 127 128 ``properties`` is an optional list of properties names. If provided, 129 only the named properties will be included in the returned properties. 130 131 ``exclude`` is an optional list of properties names. If provided, the named 132 properties will be excluded from the returned properties, even if 133 they are listed in the ``properties`` argument. 134 """ 135 field_list = [] 136 137 values = [] 138 if properties: 139 values = [document._properties[prop] for prop in properties if \ 140 prop in document._properties] 141 else: 142 values = document._properties.values() 143 values.sort(lambda a, b: cmp(a.creation_counter, b.creation_counter)) 144 145 for prop in values: 146 if properties and not prop.name in properties: 147 continue 148 if exclude and prop.name in exclude: 149 continue 150 property_class_name = prop.__class__.__name__ 151 if property_class_name in FIELDS_PROPERTES_MAPPING: 152 defaults = { 153 'required': prop.required, 154 'label': capfirst(prop.verbose_name), 155 } 156 157 if prop.default is not None: 158 defaults['initial'] = prop.default_value 159 160 if prop.choices: 161 if prop.default: 162 defaults['choices'] = prop.default_value() + list( 163 prop.choices) 164 defaults['coerce'] = prop.to_python 165 166 field_list.append((prop.name, 167 FIELDS_PROPERTES_MAPPING[property_class_name](**defaults))) 168 return SortedDict(field_list)
169
170 -class DocumentFormOptions(object):
171 - def __init__(self, options=None):
172 self.document = getattr(options, 'document', None) 173 self.properties = getattr(options, 'properties', None) 174 self.exclude = getattr(options, 'exclude', None)
175
176 -class DocumentFormMetaClass(type):
177 - def __new__(cls, name, bases, attrs):
178 try: 179 parents = [b for b in bases if issubclass(b, DocumentForm)] 180 except NameError: 181 # We are defining ModelForm itself. 182 parents = None 183 184 declared_fields = get_declared_fields(bases, attrs, False) 185 new_class = super(DocumentFormMetaClass, cls).__new__(cls, name, bases, 186 attrs) 187 188 if not parents: 189 return new_class 190 191 if 'media' not in attrs: 192 new_class.media = media_property(new_class) 193 194 opts = new_class._meta = DocumentFormOptions(getattr(new_class, 195 'Meta', None)) 196 197 if opts.document: 198 # If a document is defined, extract form fields from it. 199 fields = fields_for_document(opts.document, opts.properties, 200 opts.exclude) 201 # Override default docuemnt fields with any custom declared ones 202 # (plus, include all the other declared fields). 203 fields.update(declared_fields) 204 else: 205 fields = declared_fields 206 207 new_class.declared_fields = declared_fields 208 new_class.base_fields = fields 209 return new_class
210
211 -class BaseDocumentForm(BaseForm):
212 """ Base Document Form object """ 213
214 - def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, 215 initial=None, error_class=ErrorList, label_suffix=":", 216 empty_permitted=False, instance=None):
217 218 opts = self._meta 219 220 if instance is None: 221 self.instance = opts.document() 222 object_data = {} 223 else: 224 self.instance = instance 225 object_data = document_to_dict(instance, opts.properties, 226 opts.exclude) 227 228 if initial is not None: 229 object_data.update(initial) 230 231 super(BaseDocumentForm, self).__init__(data, files, auto_id, prefix, 232 object_data, error_class, 233 label_suffix, empty_permitted)
234
235 - def save(self, commit=True, dynamic=True):
236 """ 237 Saves this ``form``'s cleaned_data into document instance 238 ``self.instance``. 239 240 If commit=True, then the changes to ``instance`` will be saved to the 241 database. Returns ``instance``. 242 """ 243 244 opts = self._meta 245 cleaned_data = self.cleaned_data.copy() 246 for prop_name in self.instance._doc.keys(): 247 if opts.properties and prop_name not in opts.properties: 248 continue 249 if opts.exclude and prop_name in opts.exclude: 250 continue 251 if prop_name in cleaned_data: 252 value = cleaned_data.pop(prop_name) 253 if value is not None: 254 setattr(self.instance, prop_name, value) 255 256 if dynamic: 257 for attr_name in cleaned_data.keys(): 258 if opts.exclude and attr_name in opts.exclude: 259 continue 260 value = cleaned_data[attr_name] 261 if value is not None: 262 setattr(self.instance, attr_name, value) 263 264 if commit: 265 self.instance.save() 266 267 return self.instance
268
269 -class DocumentForm(BaseDocumentForm):
270 """ The document form object """ 271 __metaclass__ = DocumentFormMetaClass
272