Skip to content

Commit d9bf32c

Browse files
author
Jan Schrewe
committed
Html5 widget support; Hopefully got rid of the need for _dont_save stuff on forms
1 parent ec80e4c commit d9bf32c

File tree

6 files changed

+86
-71
lines changed

6 files changed

+86
-71
lines changed

README.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ documents.
77
Requirements
88
------------
99

10-
- Django >= 1.3
11-
- `mongoengine <http://mongoengine.org/>`__ >= 0.6
10+
- Django >= 1.4
11+
- `mongoengine <http://mongoengine.org/>`__ >= 0.8.3
1212

1313
Supported field types
1414
---------------------

mongodbforms/documentoptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ def swapped(self):
224224
225225
NOTE: Not sure this is actually usefull for documents. So at the moment
226226
it's really only here because the admin wants it. It might prove usefull
227-
for someone though, so it'S more then just a dummy.
227+
for someone though, so it's more then just a dummy.
228228
"""
229229
if self._meta.get('swappable', False):
230230
model_label = '%s.%s' % (self.app_label, self.object_name.lower())

mongodbforms/documents.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import os
22
import itertools
3-
from collections import Callable
3+
from collections import Callable, OrderedDict
44
from functools import reduce
55

6-
from django.utils.datastructures import SortedDict
76
from django.forms.forms import BaseForm, get_declared_fields, NON_FIELD_ERRORS, pretty_name
87
from django.forms.widgets import media_property
98
from django.core.exceptions import FieldError
@@ -166,8 +165,7 @@ def save_instance(form, instance, fields=None, fail_message='saved',
166165
instance.save()
167166
instance._data = data
168167
else:
169-
instance.save()
170-
168+
instance.save()
171169
return instance
172170

173171
def document_to_dict(instance, fields=None, exclude=None):
@@ -230,8 +228,15 @@ def fields_for_document(document, fields=None, exclude=None, widgets=None, \
230228

231229
if formfield:
232230
field_list.append((f.name, formfield))
231+
232+
field_dict = OrderedDict(field_list)
233+
if fields:
234+
field_dict = OrderedDict(
235+
[(f, field_dict.get(f)) for f in fields
236+
if ((not exclude) or (exclude and f not in exclude))]
237+
)
233238

234-
return SortedDict(field_list)
239+
return field_dict
235240

236241

237242

@@ -399,16 +404,24 @@ def _post_clean(self):
399404
# mongoengine chokes on empty strings for fields
400405
# that are not required. Clean them up here, though
401406
# this is maybe not the right place :-)
402-
opts._dont_save.append(f.name)
407+
print "Setting %s to None" % f.name
408+
setattr(self.instance, f.name, None)
409+
#opts._dont_save.append(f.name)
403410
except ValidationError as e:
404411
err = {f.name: [e.message]}
405412
self._update_errors(err)
406413

407414

408-
# Call the model instance's clean method.
415+
# Call validate() on the document. Since mongoengine
416+
# does not provide an argument to specify which fields
417+
# should be excluded during validation, we replace
418+
# instance._fields_ordered with a version that does
419+
# not include excluded fields. The attribute gets
420+
# restored after validation.
409421
original_fields = self.instance._fields_ordered
410-
to_check = tuple([f for f in original_fields if f not in exclude])
411-
self.instance._fields_ordered = to_check
422+
self.instance._fields_ordered = tuple(
423+
[f for f in original_fields if f not in exclude]
424+
)
412425
try:
413426
self.instance.validate()
414427
except ValidationError as e:

mongodbforms/fieldgenerator.py

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
ListField as MongoListField, MapField as MongoMapField
2222

2323
from .fields import MongoCharField, ReferenceField, DocumentMultipleChoiceField, ListField, MapField
24-
from .widgets import DynamicListWidget
24+
from .widgets import Html5SplitDateTimeWidget
2525
from .documentoptions import create_verbose_name
2626

2727
BLANK_CHOICE_DASH = [("", "---------")]
@@ -142,7 +142,7 @@ def get_field_default(self, field):
142142
d['initial'] = field.default
143143
return f.default
144144

145-
def _check_widget(self, map_key):
145+
def check_widget(self, map_key):
146146
if map_key in self.widget_override_map:
147147
return {'widget': self.widget_override_map.get(map_key)}
148148
else:
@@ -176,7 +176,7 @@ def generate_stringfield(self, field, **kwargs):
176176
defaults['regex'] = field.regex
177177

178178
form_class = self.form_field_map.get(map_key)
179-
defaults.update(self._check_widget(map_key))
179+
defaults.update(self.check_widget(map_key))
180180
defaults.update(kwargs)
181181
return form_class(**defaults)
182182

@@ -190,7 +190,7 @@ def generate_emailfield(self, field, **kwargs):
190190
'label': self.get_field_label(field),
191191
'help_text': self.get_field_help_text(field)
192192
}
193-
defaults.update(self._check_widget(map_key))
193+
defaults.update(self.check_widget(map_key))
194194
form_class = self.form_field_map.get(map_key)
195195
defaults.update(kwargs)
196196
return form_class(**defaults)
@@ -206,7 +206,7 @@ def generate_urlfield(self, field, **kwargs):
206206
'help_text': self.get_field_help_text(field)
207207
}
208208
form_class = self.form_field_map.get(map_key)
209-
defaults.update(self._check_widget(map_key))
209+
defaults.update(self.check_widget(map_key))
210210
defaults.update(kwargs)
211211
return form_class(**defaults)
212212

@@ -231,7 +231,7 @@ def generate_intfield(self, field, **kwargs):
231231
'max_value': field.max_value,
232232
})
233233
form_class = self.form_field_map.get(map_key)
234-
defaults.update(self._check_widget(map_key))
234+
defaults.update(self.check_widget(map_key))
235235
defaults.update(kwargs)
236236
return form_class(**defaults)
237237

@@ -246,7 +246,7 @@ def generate_floatfield(self, field, **kwargs):
246246
'help_text': self.get_field_help_text(field)
247247
}
248248
form_class = self.form_field_map.get(map_key)
249-
defaults.update(self._check_widget(map_key))
249+
defaults.update(self.check_widget(map_key))
250250
defaults.update(kwargs)
251251
return form_class(**defaults)
252252

@@ -262,7 +262,7 @@ def generate_decimalfield(self, field, **kwargs):
262262
'help_text': self.get_field_help_text(field)
263263
}
264264
form_class = self.form_field_map.get(map_key)
265-
defaults.update(self._check_widget(map_key))
265+
defaults.update(self.check_widget(map_key))
266266
defaults.update(kwargs)
267267
return form_class(**defaults)
268268

@@ -283,7 +283,7 @@ def generate_booleanfield(self, field, **kwargs):
283283
else:
284284
map_key = 'booleanfield'
285285
form_class = self.form_field_map.get(map_key)
286-
defaults.update(self._check_widget(map_key))
286+
defaults.update(self.check_widget(map_key))
287287
defaults.update(kwargs)
288288
return form_class(**defaults)
289289

@@ -295,7 +295,7 @@ def generate_datetimefield(self, field, **kwargs):
295295
'label': self.get_field_label(field),
296296
}
297297
form_class = self.form_field_map.get(map_key)
298-
defaults.update(self._check_widget(map_key))
298+
defaults.update(self.check_widget(map_key))
299299
defaults.update(kwargs)
300300
return form_class(**defaults)
301301

@@ -308,7 +308,7 @@ def generate_referencefield(self, field, **kwargs):
308308
'queryset': field.document_type.objects.clone(),
309309
}
310310
form_class = self.form_field_map.get(map_key)
311-
defaults.update(self._check_widget(map_key))
311+
defaults.update(self.check_widget(map_key))
312312
defaults.update(kwargs)
313313
return form_class(**defaults)
314314

@@ -340,7 +340,7 @@ def generate_listfield(self, field, **kwargs):
340340
'contained_field': form_field.__class__,
341341
})
342342
form_class = self.form_field_map.get(map_key)
343-
defaults.update(self._check_widget(map_key))
343+
defaults.update(self.check_widget(map_key))
344344
defaults.update(kwargs)
345345
return form_class(**defaults)
346346

@@ -358,7 +358,7 @@ def generate_mapfield(self, field, **kwargs):
358358
'contained_field': form_field.__class__,
359359
}
360360
form_class = self.form_field_map.get(map_key)
361-
defaults.update(self._check_widget(map_key))
361+
defaults.update(self.check_widget(map_key))
362362
defaults.update(kwargs)
363363
return form_class(**defaults)
364364

@@ -371,7 +371,7 @@ def generate_filefield(self, field, **kwargs):
371371
'help_text': self.get_field_help_text(field)
372372
}
373373
form_class = self.form_field_map.get(map_key)
374-
defaults.update(self._check_widget(map_key))
374+
defaults.update(self.check_widget(map_key))
375375
defaults.update(kwargs)
376376
return form_class(**defaults)
377377

@@ -384,7 +384,7 @@ def generate_imagefield(self, field, **kwargs):
384384
'help_text': self.get_field_help_text(field)
385385
}
386386
form_class = self.form_field_map.get(map_key)
387-
defaults.update(self._check_widget(map_key))
387+
defaults.update(self.check_widget(map_key))
388388
defaults.update(kwargs)
389389
return form_class(**defaults)
390390

@@ -416,10 +416,38 @@ def generate(self, field, **kwargs):
416416

417417
defaults.update(kwargs)
418418
return forms.CharField(**defaults)
419-
420-
class DynamicFormFieldGenerator(MongoDefaultFormFieldGenerator):
421-
widget_override_map = {
422-
'stringfield_long': forms.Textarea,
423-
'listfield': DynamicListWidget,
424-
}
425-
419+
420+
class Html5FormFieldGenerator(MongoDefaultFormFieldGenerator):
421+
def check_widget(self, map_key):
422+
override = super(Html5FormFieldGenerator, self).check_widget(map_key)
423+
if override != {}:
424+
return override
425+
426+
chunks = map_key.split('field')
427+
kind = chunks[0]
428+
429+
if kind == 'email':
430+
if hasattr(forms, 'EmailInput'):
431+
return {'widget': forms.EmailInput}
432+
else:
433+
input = forms.TextInput
434+
input.input_type = 'email'
435+
return {'widget': input}
436+
elif kind in ['int', 'float'] and len(chunks) < 2:
437+
if hasattr(forms, 'NumberInput'):
438+
return {'widget': forms.NumberInput}
439+
else:
440+
input = forms.TextInput
441+
input.input_type = 'number'
442+
return {'widget': input}
443+
elif kind == 'url':
444+
if hasattr(forms, 'URLInput'):
445+
return {'widget': forms.URLInput}
446+
else:
447+
input = forms.TextInput
448+
input.input_type = 'url'
449+
return {'widget': input}
450+
elif kind == 'datetime':
451+
return {'widget': Html5SplitDateTimeWidget}
452+
else:
453+
return {}

mongodbforms/widgets.py

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
import copy
22

3-
from django.forms.widgets import Widget, Media, TextInput
3+
from django.forms.widgets import Widget, Media, TextInput, SplitDateTimeWidget, DateInput, TimeInput, MultiWidget
44
from django.utils.safestring import mark_safe
55
from django.core.validators import EMPTY_VALUES
66
from django.forms.util import flatatt
77

8+
class Html5SplitDateTimeWidget(SplitDateTimeWidget):
9+
def __init__(self, attrs=None, date_format=None, time_format=None):
10+
date_input = DateInput(attrs=attrs, format=date_format)
11+
date_input.input_type = 'date'
12+
time_input = TimeInput(attrs=attrs, format=time_format)
13+
time_input.input_type = 'time'
14+
widgets = (date_input, time_input)
15+
MultiWidget.__init__(self, widgets, attrs)
16+
817
class ListWidget(Widget):
918
def __init__(self, contained_widget, attrs=None):
1019
self.contained_widget = contained_widget
@@ -73,41 +82,6 @@ def __deepcopy__(self, memo):
7382
obj.contained_widget = copy.deepcopy(self.contained_widget)
7483
#obj.widget_type = copy.deepcopy(self.widget_type)
7584
return obj
76-
77-
class DynamicListWidget(ListWidget):
78-
def __init__(self, widget_type, attrs=None):
79-
class_attr = {'class': 'dynamiclistwidget'}
80-
if attrs is None:
81-
attrs = class_attr
82-
elif 'class' in attrs:
83-
attrs['class'] = '%s %s' % (attrs['class'], class_attr['class'])
84-
else:
85-
attrs.update(class_attr)
86-
super(DynamicListWidget, self).__init__(widget_type, attrs)
87-
88-
def format_output(self, rendered_widgets):
89-
"""
90-
Given a list of rendered widgets (as strings), returns a Unicode string
91-
representing the HTML for the whole lot.
92-
93-
This hook allows you to format the HTML design of the widgets, if
94-
needed.
95-
"""
96-
#print(rendered_widgets)
97-
output = []
98-
for widget in rendered_widgets:
99-
output.append("<p>%s</p>" % widget)
100-
#output.append('<script type="text/javascript">mdbf.onDomReady(mdbf.init("field-%s"))</script>' % self._name)
101-
return ''.join(output)
102-
103-
def _get_media(self):
104-
"Media for a multiwidget is the combination of all media of the subwidgets"
105-
media = Media(js=('mongodbforms/dynamiclistwidget.js',))
106-
for w in self.widgets:
107-
media = media + w.media
108-
return media
109-
media = property(_get_media)
110-
11185

11286
class MapWidget(Widget):
11387
"""

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def convert_readme():
1111
return open('README.txt').read()
1212

1313
setup(name='mongodbforms',
14-
version='0.2.2',
14+
version='0.3',
1515
description="An implementation of django forms using mongoengine.",
1616
author='Jan Schrewe',
1717
author_email='jan@schafproductions.com',

0 commit comments

Comments
 (0)