PageRenderTime 22ms CodeModel.GetById 2ms app.highlight 12ms RepoModel.GetById 2ms app.codeStats 0ms

/dojango/forms/models.py

http://dojango.googlecode.com/
Python | 212 lines | 187 code | 10 blank | 15 comment | 3 complexity | 871b581bb42f3bf3b11f78effb72987a MD5 | raw file
  1from django.forms import *
  2from django.forms.models import BaseModelFormSet
  3from django.forms.models import BaseInlineFormSet
  4from django.forms.models import ModelChoiceIterator
  5from django.forms.models import InlineForeignKeyHiddenInput, InlineForeignKeyField
  6
  7from django.utils.text import capfirst
  8
  9from formsets import BaseFormSet
 10
 11from django.db.models import fields
 12
 13from dojango.forms.fields import *
 14from dojango.forms.widgets import DojoWidgetMixin, Textarea, Select, SelectMultiple, HiddenInput
 15
 16__all__ = (
 17    'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
 18    'save_instance', 'ModelChoiceField', 'ModelMultipleChoiceField',
 19)
 20    
 21class ModelChoiceField(DojoFieldMixin, models.ModelChoiceField):
 22    """
 23    Overwritten 'ModelChoiceField' using the 'DojoFieldMixin' functionality.
 24    """
 25    widget = Select
 26
 27class ModelMultipleChoiceField(DojoFieldMixin, models.ModelMultipleChoiceField):
 28    """
 29    Overwritten 'ModelMultipleChoiceField' using the 'DojoFieldMixin' functonality.
 30    """
 31    widget = SelectMultiple
 32
 33# Fields #####################################################################
 34
 35class InlineForeignKeyHiddenInput(DojoWidgetMixin, InlineForeignKeyHiddenInput):
 36    """
 37    Overwritten InlineForeignKeyHiddenInput to use the dojango widget mixin
 38    """
 39    dojo_type = 'dijit.form.TextBox' # otherwise dijit.form.Form can't get its values
 40
 41class InlineForeignKeyField(DojoFieldMixin, InlineForeignKeyField, Field):
 42    """
 43    Overwritten InlineForeignKeyField to use the dojango field mixin and passing
 44    the dojango InlineForeignKeyHiddenInput as widget.
 45    """
 46    def __init__(self, parent_instance, *args, **kwargs):
 47        self.parent_instance = parent_instance
 48        self.pk_field = kwargs.pop("pk_field", False)
 49        self.to_field = kwargs.pop("to_field", None)
 50        if self.parent_instance is not None:
 51            if self.to_field:
 52                kwargs["initial"] = getattr(self.parent_instance, self.to_field)
 53            else:
 54                kwargs["initial"] = self.parent_instance.pk
 55
 56        kwargs["required"] = False
 57        kwargs["widget"] = InlineForeignKeyHiddenInput
 58        # don't call the the superclass of this one. Use the superclass of the 
 59        # normal django InlineForeignKeyField
 60        Field.__init__(self, *args, **kwargs)
 61
 62# our customized model field => form field map
 63# here it is defined which form field is used by which model field, when creating a ModelForm
 64MODEL_TO_FORM_FIELD_MAP = (
 65    # (model_field, form_field, [optional widget])
 66    # the order of these fields is very important for inherited model fields
 67    # e.g. the CharField must be checked at last, because several other
 68    # fields are a subclass of it.
 69    (fields.CommaSeparatedIntegerField, CharField),
 70    (fields.DateTimeField, DateTimeField), # must be in front of the DateField
 71    (fields.DateField, DateField),
 72    (fields.DecimalField, DecimalField),
 73    (fields.EmailField, EmailField),
 74    (fields.FilePathField, FilePathField),
 75    (fields.FloatField, FloatField),
 76    (fields.related.ForeignKey, ModelChoiceField),
 77    (fields.files.ImageField, ImageField),
 78    (fields.files.FileField, FileField),
 79    (fields.IPAddressField, IPAddressField),
 80    (fields.related.ManyToManyField, ModelMultipleChoiceField),
 81    (fields.NullBooleanField, CharField),
 82    (fields.BooleanField, BooleanField),
 83    (fields.PositiveSmallIntegerField, IntegerField),
 84    (fields.PositiveIntegerField, IntegerField),
 85    (fields.SlugField, SlugField),
 86    (fields.SmallIntegerField, IntegerField),
 87    (fields.IntegerField, IntegerField),
 88    (fields.TimeField, TimeField),
 89    (fields.URLField, URLField),
 90    (fields.TextField, CharField, Textarea),
 91    (fields.CharField, CharField),
 92)
 93
 94def formfield_function(field, **kwargs):
 95    """
 96    Custom formfield function, so we can inject our own form fields. The 
 97    mapping of model fields to form fields is defined in 'MODEL_TO_FORM_FIELD_MAP'.
 98    It uses the default django mapping as fallback, if there is no match in our
 99    custom map.
100    
101    field -- a model field
102    """
103    for field_map in MODEL_TO_FORM_FIELD_MAP:
104        if isinstance(field, field_map[0]):
105            defaults = {}
106            if field.choices:
107                # the normal django field forms.TypedChoiceField is wired hard
108                # within the original db/models/fields.py.
109                # If we use our custom Select widget, we also have to pass in
110                # some additional validation field attributes.
111                defaults['widget'] = Select(attrs={
112                    'extra_field_attrs':{
113                        'required':not field.blank,
114                        'help_text':field.help_text,
115                    }
116                })
117            elif len(field_map) == 3:
118                defaults['widget']=field_map[2]
119            defaults.update(kwargs)
120            return field.formfield(form_class=field_map[1], **defaults)
121    # return the default formfield, if there is no equivalent
122    return field.formfield(**kwargs)
123
124# ModelForms #################################################################
125
126def fields_for_model(*args, **kwargs):
127    """Changed fields_for_model function, where we use our own formfield_callback"""
128    kwargs["formfield_callback"] = formfield_function
129    return models.fields_for_model(*args, **kwargs)
130
131class ModelFormMetaclass(models.ModelFormMetaclass):
132    """
133    Overwritten 'ModelFormMetaClass'. We attach our own formfield generation
134    function.
135    """
136    def __new__(cls, name, bases, attrs):
137        # this is how we can replace standard django form fields with dojo ones
138        attrs["formfield_callback"] = formfield_function
139        return super(ModelFormMetaclass, cls).__new__(cls, name, bases, attrs)
140
141class ModelForm(models.ModelForm):
142    """
143    Overwritten 'ModelForm' using the metaclass defined above.
144    """
145    __metaclass__ = ModelFormMetaclass
146
147def modelform_factory(*args, **kwargs):
148    """Changed modelform_factory function, where we use our own formfield_callback"""
149    kwargs["formfield_callback"] = formfield_function
150    kwargs["form"] = ModelForm
151    return models.modelform_factory(*args, **kwargs)
152
153# ModelFormSets ##############################################################
154
155class BaseModelFormSet(BaseModelFormSet, BaseFormSet):
156    
157    def add_fields(self, form, index):
158        """Overwritten BaseModelFormSet using the dojango BaseFormSet and
159        the ModelChoiceField. 
160        NOTE: This method was copied from django 1.3 beta 1"""
161        from django.db.models import AutoField, OneToOneField, ForeignKey
162        self._pk_field = pk = self.model._meta.pk
163        def pk_is_not_editable(pk):
164            return ((not pk.editable) or (pk.auto_created or isinstance(pk, AutoField))
165                or (pk.rel and pk.rel.parent_link and pk_is_not_editable(pk.rel.to._meta.pk)))
166        if pk_is_not_editable(pk) or pk.name not in form.fields:
167            if form.is_bound:
168                pk_value = form.instance.pk
169            else:
170                try:
171                    if index is not None:
172                        pk_value = self.get_queryset()[index].pk
173                    else:
174                        pk_value = None
175                except IndexError:
176                    pk_value = None
177            if isinstance(pk, OneToOneField) or isinstance(pk, ForeignKey):
178                qs = pk.rel.to._default_manager.get_query_set()
179            else:
180                qs = self.model._default_manager.get_query_set()
181            qs = qs.using(form.instance._state.db)
182            form.fields[self._pk_field.name] = ModelChoiceField(qs, initial=pk_value, required=False, widget=HiddenInput)
183        BaseFormSet.add_fields(self, form, index)
184
185def modelformset_factory(*args, **kwargs):
186    """Changed modelformset_factory function, where we use our own formfield_callback"""
187    kwargs["formfield_callback"] = formfield_function
188    kwargs["formset"] = BaseModelFormSet
189    return models.modelformset_factory(*args, **kwargs)
190
191# InlineFormSets #############################################################
192
193class BaseInlineFormSet(BaseInlineFormSet, BaseModelFormSet):
194    """Overwritten BaseInlineFormSet using the dojango InlineForeignKeyFields.
195    NOTE: This method was copied from django 1.1"""
196    def add_fields(self, form, index):
197        super(BaseInlineFormSet, self).add_fields(form, index)
198        if self._pk_field == self.fk:
199            form.fields[self._pk_field.name] = InlineForeignKeyField(self.instance, pk_field=True)
200        else:
201            kwargs = {
202                'label': getattr(form.fields.get(self.fk.name), 'label', capfirst(self.fk.verbose_name))
203            }
204            if self.fk.rel.field_name != self.fk.rel.to._meta.pk.name:
205                kwargs['to_field'] = self.fk.rel.field_name
206            form.fields[self.fk.name] = InlineForeignKeyField(self.instance, **kwargs)
207            
208def inlineformset_factory(*args, **kwargs):
209    """Changed inlineformset_factory function, where we use our own formfield_callback"""
210    kwargs["formfield_callback"] = formfield_function
211    kwargs["formset"] = BaseInlineFormSet
212    return models.inlineformset_factory(*args, **kwargs)