123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464 |
- # -*- coding: utf-8 -*-
- """
- Based on django mongotools (https://github.com/wpjunior/django-mongotools) by
- Wilson Júnior (wilsonpjunior@gmail.com).
- """
- import collections
- from django import forms
- from django.core.validators import EMPTY_VALUES
- try:
- from django.utils.encoding import smart_text as smart_unicode
- except ImportError:
- try:
- from django.utils.encoding import smart_unicode
- except ImportError:
- from django.forms.util import smart_unicode
- from django.utils.text import capfirst
- from mongoengine import (ReferenceField as MongoReferenceField,
- EmbeddedDocumentField as MongoEmbeddedDocumentField,
- ListField as MongoListField,
- MapField as MongoMapField)
- from mongodbforms.fields import (MongoCharField, MongoEmailField,
- MongoURLField, ReferenceField,
- DocumentMultipleChoiceField, ListField,
- MapField)
- from mongodbforms.widgets import Html5SplitDateTimeWidget
- from mongodbforms.documentoptions import create_verbose_name
- BLANK_CHOICE_DASH = [("", "---------")]
- class MongoFormFieldGenerator(object):
- """This class generates Django form-fields for mongoengine-fields."""
-
- # used for fields that fit in one of the generate functions
- # but don't actually have the name.
- generator_map = {
- 'sortedlistfield': 'generate_listfield',
- 'longfield': 'generate_intfield',
- }
-
- form_field_map = {
- 'stringfield': MongoCharField,
- 'stringfield_choices': forms.TypedChoiceField,
- 'stringfield_long': MongoCharField,
- 'emailfield': MongoEmailField,
- 'urlfield': MongoURLField,
- 'intfield': forms.IntegerField,
- 'intfield_choices': forms.TypedChoiceField,
- 'floatfield': forms.FloatField,
- 'decimalfield': forms.DecimalField,
- 'booleanfield': forms.BooleanField,
- 'booleanfield_choices': forms.TypedChoiceField,
- 'datetimefield': forms.SplitDateTimeField,
- 'referencefield': ReferenceField,
- 'listfield': ListField,
- 'listfield_choices': forms.MultipleChoiceField,
- 'listfield_references': DocumentMultipleChoiceField,
- 'mapfield': MapField,
- 'filefield': forms.FileField,
- 'imagefield': forms.ImageField,
- }
-
- # uses the same keys as form_field_map
- widget_override_map = {
- 'stringfield_long': forms.Textarea,
- }
-
- def __init__(self, field_overrides={}, widget_overrides={}):
- self.form_field_map.update(field_overrides)
- self.widget_override_map.update(widget_overrides)
- def generate(self, field, **kwargs):
- """Tries to lookup a matching formfield generator (lowercase
- field-classname) and raises a NotImplementedError of no generator
- can be found.
- """
- # do not handle embedded documents here. They are more or less special
- # and require some form of inline formset or something more complex
- # to handle then a simple field
- if isinstance(field, MongoEmbeddedDocumentField):
- return
-
- attr_name = 'generate_%s' % field.__class__.__name__.lower()
- if hasattr(self, attr_name):
- return getattr(self, attr_name)(field, **kwargs)
- for cls in field.__class__.__bases__:
- cls_name = cls.__name__.lower()
-
- attr_name = 'generate_%s' % cls_name
- if hasattr(self, attr_name):
- return getattr(self, attr_name)(field, **kwargs)
- if cls_name in self.form_field_map:
- attr = self.generator_map.get(cls_name)
- return getattr(self, attr)(field, **kwargs)
-
- raise NotImplementedError('%s is not supported by MongoForm' %
- field.__class__.__name__)
- def get_field_choices(self, field, include_blank=True,
- blank_choice=BLANK_CHOICE_DASH):
- first_choice = include_blank and blank_choice or []
- return first_choice + list(field.choices)
- def string_field(self, value):
- if value in EMPTY_VALUES:
- return None
- return smart_unicode(value)
- def integer_field(self, value):
- if value in EMPTY_VALUES:
- return None
- return int(value)
- def boolean_field(self, value):
- if value in EMPTY_VALUES:
- return None
- return value.lower() == 'true'
- def get_field_label(self, field):
- if field.verbose_name:
- return capfirst(field.verbose_name)
- if field.name is not None:
- return capfirst(create_verbose_name(field.name))
- return ''
- def get_field_help_text(self, field):
- if field.help_text:
- return field.help_text
- else:
- return ''
-
- def get_field_default(self, field):
- if isinstance(field, (MongoListField, MongoMapField)):
- f = field.field
- else:
- f = field
- d = {}
- if isinstance(f.default, collections.Callable):
- d['initial'] = field.default()
- d['show_hidden_initial'] = True
- return f.default()
- else:
- d['initial'] = field.default
- return f.default
-
- def check_widget(self, map_key):
- if map_key in self.widget_override_map:
- return {'widget': self.widget_override_map.get(map_key)}
- else:
- return {}
- def generate_stringfield(self, field, **kwargs):
- defaults = {
- 'label': self.get_field_label(field),
- 'initial': self.get_field_default(field),
- 'required': field.required,
- 'help_text': self.get_field_help_text(field),
- }
- if field.choices:
- map_key = 'stringfield_choices'
- defaults.update({
- 'choices': self.get_field_choices(field),
- 'coerce': self.string_field,
- })
- elif field.max_length is None:
- map_key = 'stringfield_long'
- defaults.update({
- 'min_length': field.min_length,
- })
- else:
- map_key = 'stringfield'
- defaults.update({
- 'max_length': field.max_length,
- 'min_length': field.min_length,
- })
- if field.regex:
- defaults['regex'] = field.regex
-
- form_class = self.form_field_map.get(map_key)
- defaults.update(self.check_widget(map_key))
- defaults.update(kwargs)
- return form_class(**defaults)
- def generate_emailfield(self, field, **kwargs):
- map_key = 'emailfield'
- defaults = {
- 'required': field.required,
- 'min_length': field.min_length,
- 'max_length': field.max_length,
- 'initial': self.get_field_default(field),
- 'label': self.get_field_label(field),
- 'help_text': self.get_field_help_text(field)
- }
- defaults.update(self.check_widget(map_key))
- form_class = self.form_field_map.get(map_key)
- defaults.update(kwargs)
- return form_class(**defaults)
- def generate_urlfield(self, field, **kwargs):
- map_key = 'urlfield'
- defaults = {
- 'required': field.required,
- 'min_length': field.min_length,
- 'max_length': field.max_length,
- 'initial': self.get_field_default(field),
- 'label': self.get_field_label(field),
- 'help_text': self.get_field_help_text(field)
- }
- form_class = self.form_field_map.get(map_key)
- defaults.update(self.check_widget(map_key))
- defaults.update(kwargs)
- return form_class(**defaults)
- def generate_intfield(self, field, **kwargs):
- defaults = {
- 'required': field.required,
- 'initial': self.get_field_default(field),
- 'label': self.get_field_label(field),
- 'help_text': self.get_field_help_text(field)
- }
- if field.choices:
- map_key = 'intfield_choices'
- defaults.update({
- 'coerce': self.integer_field,
- 'empty_value': None,
- 'choices': self.get_field_choices(field),
- })
- else:
- map_key = 'intfield'
- defaults.update({
- 'min_value': field.min_value,
- 'max_value': field.max_value,
- })
- form_class = self.form_field_map.get(map_key)
- defaults.update(self.check_widget(map_key))
- defaults.update(kwargs)
- return form_class(**defaults)
- def generate_floatfield(self, field, **kwargs):
- map_key = 'floatfield'
- defaults = {
- 'label': self.get_field_label(field),
- 'initial': self.get_field_default(field),
- 'required': field.required,
- 'min_value': field.min_value,
- 'max_value': field.max_value,
- 'help_text': self.get_field_help_text(field)
- }
- form_class = self.form_field_map.get(map_key)
- defaults.update(self.check_widget(map_key))
- defaults.update(kwargs)
- return form_class(**defaults)
- def generate_decimalfield(self, field, **kwargs):
- map_key = 'decimalfield'
- defaults = {
- 'label': self.get_field_label(field),
- 'initial': self.get_field_default(field),
- 'required': field.required,
- 'min_value': field.min_value,
- 'max_value': field.max_value,
- 'decimal_places': field.precision,
- 'help_text': self.get_field_help_text(field)
- }
- form_class = self.form_field_map.get(map_key)
- defaults.update(self.check_widget(map_key))
- defaults.update(kwargs)
- return form_class(**defaults)
- def generate_booleanfield(self, field, **kwargs):
- defaults = {
- 'required': field.required,
- 'initial': self.get_field_default(field),
- 'label': self.get_field_label(field),
- 'help_text': self.get_field_help_text(field)
- }
- if field.choices:
- map_key = 'booleanfield_choices'
- defaults.update({
- 'coerce': self.boolean_field,
- 'empty_value': None,
- 'choices': self.get_field_choices(field),
- })
- else:
- map_key = 'booleanfield'
- form_class = self.form_field_map.get(map_key)
- defaults.update(self.check_widget(map_key))
- defaults.update(kwargs)
- return form_class(**defaults)
- def generate_datetimefield(self, field, **kwargs):
- map_key = 'datetimefield'
- defaults = {
- 'required': field.required,
- 'initial': self.get_field_default(field),
- 'label': self.get_field_label(field),
- }
- form_class = self.form_field_map.get(map_key)
- defaults.update(self.check_widget(map_key))
- defaults.update(kwargs)
- return form_class(**defaults)
- def generate_referencefield(self, field, **kwargs):
- map_key = 'referencefield'
- defaults = {
- 'label': self.get_field_label(field),
- 'help_text': self.get_field_help_text(field),
- 'required': field.required,
- 'queryset': field.document_type.objects.clone(),
- }
- form_class = self.form_field_map.get(map_key)
- defaults.update(self.check_widget(map_key))
- defaults.update(kwargs)
- return form_class(**defaults)
- def generate_listfield(self, field, **kwargs):
- # We can't really handle embedded documents here.
- # So we just ignore them
- if isinstance(field.field, MongoEmbeddedDocumentField):
- return
-
- defaults = {
- 'label': self.get_field_label(field),
- 'help_text': self.get_field_help_text(field),
- 'required': field.required,
- }
- if field.field.choices:
- map_key = 'listfield_choices'
- defaults.update({
- 'choices': field.field.choices,
- 'widget': forms.CheckboxSelectMultiple
- })
- elif isinstance(field.field, MongoReferenceField):
- map_key = 'listfield_references'
- defaults.update({
- 'queryset': field.field.document_type.objects.clone(),
- })
- else:
- map_key = 'listfield'
- form_field = self.generate(field.field)
- defaults.update({
- 'contained_field': form_field.__class__,
- })
- form_class = self.form_field_map.get(map_key)
- defaults.update(self.check_widget(map_key))
- defaults.update(kwargs)
- return form_class(**defaults)
-
- def generate_mapfield(self, field, **kwargs):
- # We can't really handle embedded documents here.
- # So we just ignore them
- if isinstance(field.field, MongoEmbeddedDocumentField):
- return
-
- map_key = 'mapfield'
- form_field = self.generate(field.field)
- defaults = {
- 'label': self.get_field_label(field),
- 'help_text': self.get_field_help_text(field),
- 'required': field.required,
- 'contained_field': form_field.__class__,
- }
- form_class = self.form_field_map.get(map_key)
- defaults.update(self.check_widget(map_key))
- defaults.update(kwargs)
- return form_class(**defaults)
- def generate_filefield(self, field, **kwargs):
- map_key = 'filefield'
- defaults = {
- 'required': field.required,
- 'label': self.get_field_label(field),
- 'initial': self.get_field_default(field),
- 'help_text': self.get_field_help_text(field)
- }
- form_class = self.form_field_map.get(map_key)
- defaults.update(self.check_widget(map_key))
- defaults.update(kwargs)
- return form_class(**defaults)
- def generate_imagefield(self, field, **kwargs):
- map_key = 'imagefield'
- defaults = {
- 'required': field.required,
- 'label': self.get_field_label(field),
- 'initial': self.get_field_default(field),
- 'help_text': self.get_field_help_text(field)
- }
- form_class = self.form_field_map.get(map_key)
- defaults.update(self.check_widget(map_key))
- defaults.update(kwargs)
- return form_class(**defaults)
- class MongoDefaultFormFieldGenerator(MongoFormFieldGenerator):
- """This class generates Django form-fields for mongoengine-fields."""
- def generate(self, field, **kwargs):
- """Tries to lookup a matching formfield generator (lowercase
- field-classname) and raises a NotImplementedError of no generator
- can be found.
- """
- try:
- sup = super(MongoDefaultFormFieldGenerator, self)
- return sup.generate(field, **kwargs)
- except NotImplementedError:
- # a normal charfield is always a good guess
- # for a widget.
- # TODO: Somehow add a warning
- defaults = {'required': field.required}
- if hasattr(field, 'min_length'):
- defaults['min_length'] = field.min_length
- if hasattr(field, 'max_length'):
- defaults['max_length'] = field.max_length
- if hasattr(field, 'default'):
- defaults['initial'] = field.default
- defaults.update(kwargs)
- return forms.CharField(**defaults)
- class Html5FormFieldGenerator(MongoDefaultFormFieldGenerator):
- def check_widget(self, map_key):
- override = super(Html5FormFieldGenerator, self).check_widget(map_key)
- if override != {}:
- return override
-
- chunks = map_key.split('field')
- kind = chunks[0]
-
- if kind == 'email':
- if hasattr(forms, 'EmailInput'):
- return {'widget': forms.EmailInput}
- else:
- input = forms.TextInput
- input.input_type = 'email'
- return {'widget': input}
- elif kind in ['int', 'float'] and len(chunks) < 2:
- if hasattr(forms, 'NumberInput'):
- return {'widget': forms.NumberInput}
- else:
- input = forms.TextInput
- input.input_type = 'number'
- return {'widget': input}
- elif kind == 'url':
- if hasattr(forms, 'URLInput'):
- return {'widget': forms.URLInput}
- else:
- input = forms.TextInput
- input.input_type = 'url'
- return {'widget': input}
- elif kind == 'datetime':
- return {'widget': Html5SplitDateTimeWidget}
- else:
- return {}
|